]> Zhao Yanbai Git Server - minix.git/commitdiff
Import makefs from NetBSD 87/2987/2
authorJean-Baptiste Boric <jblbeurope@gmail.com>
Thu, 10 Sep 2015 21:45:24 +0000 (23:45 +0200)
committerLionel Sambuc <lionel.sambuc@gmail.com>
Wed, 7 Oct 2015 10:39:44 +0000 (12:39 +0200)
Change-Id: I0ebcc9d0168df9d26cfb0af0fce2bc894ce688af

122 files changed:
distrib/sets/lists/minix/mi
sbin/Makefile
sbin/mount/fattr.c [new file with mode: 0644]
sbin/mount/mountprog.h [new file with mode: 0644]
sbin/newfs_msdos/Makefile [new file with mode: 0644]
sbin/newfs_msdos/mkfs_msdos.c [new file with mode: 0644]
sbin/newfs_msdos/mkfs_msdos.h [new file with mode: 0644]
sbin/newfs_msdos/newfs_msdos.8 [new file with mode: 0644]
sbin/newfs_msdos/newfs_msdos.c [new file with mode: 0644]
sbin/newfs_udf/Makefile [new file with mode: 0644]
sbin/newfs_udf/newfs_udf.8 [new file with mode: 0644]
sbin/newfs_udf/newfs_udf.c [new file with mode: 0644]
sbin/newfs_udf/newfs_udf.h [new file with mode: 0644]
sbin/newfs_udf/udf_create.c [new file with mode: 0644]
sbin/newfs_udf/udf_create.h [new file with mode: 0644]
sbin/newfs_udf/udf_write.c [new file with mode: 0644]
sbin/newfs_udf/udf_write.h [new file with mode: 0644]
sbin/newfs_udf/unicode.h [new file with mode: 0644]
sbin/newfs_v7fs/Makefile [new file with mode: 0644]
sbin/newfs_v7fs/main.c [new file with mode: 0644]
sbin/newfs_v7fs/newfs_v7fs.8 [new file with mode: 0644]
sbin/newfs_v7fs/newfs_v7fs.c [new file with mode: 0644]
sbin/newfs_v7fs/newfs_v7fs.h [new file with mode: 0644]
sys/fs/Makefile
sys/fs/udf/Makefile [new file with mode: 0644]
sys/fs/udf/ecma167-udf.h [new file with mode: 0644]
sys/fs/udf/files.udf [new file with mode: 0644]
sys/fs/udf/udf.h [new file with mode: 0644]
sys/fs/udf/udf_allocation.c [new file with mode: 0644]
sys/fs/udf/udf_bswap.h [new file with mode: 0644]
sys/fs/udf/udf_mount.h [new file with mode: 0644]
sys/fs/udf/udf_osta.c [new file with mode: 0644]
sys/fs/udf/udf_osta.h [new file with mode: 0644]
sys/fs/udf/udf_readwrite.c [new file with mode: 0644]
sys/fs/udf/udf_rename.c [new file with mode: 0644]
sys/fs/udf/udf_strat_bootstrap.c [new file with mode: 0644]
sys/fs/udf/udf_strat_direct.c [new file with mode: 0644]
sys/fs/udf/udf_strat_rmw.c [new file with mode: 0644]
sys/fs/udf/udf_strat_sequential.c [new file with mode: 0644]
sys/fs/udf/udf_subr.c [new file with mode: 0644]
sys/fs/udf/udf_subr.h [new file with mode: 0644]
sys/fs/udf/udf_vfsops.c [new file with mode: 0644]
sys/fs/udf/udf_vnops.c [new file with mode: 0644]
sys/fs/v7fs/Makefile [new file with mode: 0644]
sys/fs/v7fs/files.v7fs [new file with mode: 0644]
sys/fs/v7fs/v7fs.h [new file with mode: 0644]
sys/fs/v7fs/v7fs_args.h [new file with mode: 0644]
sys/fs/v7fs/v7fs_datablock.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_datablock.h [new file with mode: 0644]
sys/fs/v7fs/v7fs_dirent.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_dirent.h [new file with mode: 0644]
sys/fs/v7fs/v7fs_endian.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_endian.h [new file with mode: 0644]
sys/fs/v7fs/v7fs_extern.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_extern.h [new file with mode: 0644]
sys/fs/v7fs/v7fs_file.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_file.h [new file with mode: 0644]
sys/fs/v7fs/v7fs_file_util.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_impl.h [new file with mode: 0644]
sys/fs/v7fs/v7fs_inode.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_inode.h [new file with mode: 0644]
sys/fs/v7fs/v7fs_inode_util.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_io.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_io_kern.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_io_user.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_superblock.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_superblock.h [new file with mode: 0644]
sys/fs/v7fs/v7fs_superblock_util.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_vfsops.c [new file with mode: 0644]
sys/fs/v7fs/v7fs_vnops.c [new file with mode: 0644]
usr.sbin/Makefile
usr.sbin/makefs/Makefile [new file with mode: 0644]
usr.sbin/makefs/README [new file with mode: 0644]
usr.sbin/makefs/TODO [new file with mode: 0644]
usr.sbin/makefs/cd9660.c [new file with mode: 0644]
usr.sbin/makefs/cd9660.h [new file with mode: 0644]
usr.sbin/makefs/cd9660/Makefile.inc [new file with mode: 0644]
usr.sbin/makefs/cd9660/cd9660_archimedes.c [new file with mode: 0644]
usr.sbin/makefs/cd9660/cd9660_archimedes.h [new file with mode: 0644]
usr.sbin/makefs/cd9660/cd9660_conversion.c [new file with mode: 0644]
usr.sbin/makefs/cd9660/cd9660_debug.c [new file with mode: 0644]
usr.sbin/makefs/cd9660/cd9660_eltorito.c [new file with mode: 0644]
usr.sbin/makefs/cd9660/cd9660_eltorito.h [new file with mode: 0644]
usr.sbin/makefs/cd9660/cd9660_strings.c [new file with mode: 0644]
usr.sbin/makefs/cd9660/cd9660_write.c [new file with mode: 0644]
usr.sbin/makefs/cd9660/iso9660_rrip.c [new file with mode: 0644]
usr.sbin/makefs/cd9660/iso9660_rrip.h [new file with mode: 0644]
usr.sbin/makefs/chfs.c [new file with mode: 0644]
usr.sbin/makefs/chfs/Makefile.inc [new file with mode: 0644]
usr.sbin/makefs/chfs/chfs_mkfs.c [new file with mode: 0644]
usr.sbin/makefs/chfs/chfs_mkfs.h [new file with mode: 0644]
usr.sbin/makefs/chfs_makefs.h [new file with mode: 0644]
usr.sbin/makefs/ffs.c [new file with mode: 0644]
usr.sbin/makefs/ffs.h [new file with mode: 0644]
usr.sbin/makefs/ffs/Makefile.inc [new file with mode: 0644]
usr.sbin/makefs/ffs/buf.c [new file with mode: 0644]
usr.sbin/makefs/ffs/buf.h [new file with mode: 0644]
usr.sbin/makefs/ffs/ffs_alloc.c [new file with mode: 0644]
usr.sbin/makefs/ffs/ffs_balloc.c [new file with mode: 0644]
usr.sbin/makefs/ffs/ffs_extern.h [new file with mode: 0644]
usr.sbin/makefs/ffs/mkfs.c [new file with mode: 0644]
usr.sbin/makefs/ffs/newfs_extern.h [new file with mode: 0644]
usr.sbin/makefs/ffs/ufs_bmap.c [new file with mode: 0644]
usr.sbin/makefs/ffs/ufs_inode.h [new file with mode: 0644]
usr.sbin/makefs/makefs.8 [new file with mode: 0644]
usr.sbin/makefs/makefs.c [new file with mode: 0644]
usr.sbin/makefs/makefs.h [new file with mode: 0644]
usr.sbin/makefs/msdos.c [new file with mode: 0644]
usr.sbin/makefs/msdos.h [new file with mode: 0644]
usr.sbin/makefs/msdos/Makefile.inc [new file with mode: 0644]
usr.sbin/makefs/msdos/msdosfs_denode.c [new file with mode: 0644]
usr.sbin/makefs/msdos/msdosfs_vfsops.c [new file with mode: 0644]
usr.sbin/makefs/msdos/msdosfs_vnops.c [new file with mode: 0644]
usr.sbin/makefs/udf.c [new file with mode: 0644]
usr.sbin/makefs/udf/Makefile.inc [new file with mode: 0644]
usr.sbin/makefs/udf/cdio_mmc_structs.h [new file with mode: 0644]
usr.sbin/makefs/v7fs.c [new file with mode: 0644]
usr.sbin/makefs/v7fs/Makefile.inc [new file with mode: 0644]
usr.sbin/makefs/v7fs/v7fs_estimate.c [new file with mode: 0644]
usr.sbin/makefs/v7fs/v7fs_populate.c [new file with mode: 0644]
usr.sbin/makefs/v7fs_makefs.h [new file with mode: 0644]
usr.sbin/makefs/walk.c [new file with mode: 0644]

index 05771c69c2d0842445b3637b4874929d6eda7af0..63d78d958e491c433f7273d6cf445260e0530225 100644 (file)
 ./sbin/mkfs.mfs                                minix-sys
 ./sbin/mknod                           minix-sys
 ./sbin/newfs_ext2fs                    minix-sys
+./sbin/newfs_msdos                     minix-sys
+./sbin/newfs_udf                       minix-sys
+./sbin/newfs_v7fs                      minix-sys
 ./sbin/nologin                         minix-sys
 ./sbin/ping                            minix-sys
 ./sbin/poweroff                                minix-sys
 ./usr/include/fs                       minix-sys
 ./usr/include/fs/puffs                 minix-sys
 ./usr/include/fs/puffs/puffs_msgif.h   minix-sys
+./usr/include/fs/udf                   minix-sys
+./usr/include/fs/udf/ecma167-udf.h     minix-sys
+./usr/include/fs/udf/udf_mount.h       minix-sys
+./usr/include/fs/v7fs                  minix-sys
+./usr/include/fs/v7fs/v7fs_args.h      minix-sys
+./usr/include/fs/v7fs/v7fs.h           minix-sys
 ./usr/include/fstab.h                  minix-sys
 ./usr/include/fts.h                    minix-sys
 ./usr/include/ftw.h                    minix-sys
 ./usr/man/man8/link.8                          minix-sys
 ./usr/man/man8/loadramdisk.8                   minix-sys
 ./usr/man/man8/MAKEDEV.8                       minix-sys
+./usr/man/man8/makefs.8                        minix-sys
 ./usr/man/man8/makewhatis.8                    minix-sys
 ./usr/man/man8/mknod.8                         minix-sys
 ./usr/man/man8/mtree.8                         minix-sys
 ./usr/man/man8/netconf.8                       minix-sys
 ./usr/man/man8/newfs_ext2fs.8                  minix-sys
+./usr/man/man8/newfs_msdos.8                   minix-sys
+./usr/man/man8/newfs_udf.8                     minix-sys
+./usr/man/man8/newfs_v7fs.8                    minix-sys
 ./usr/man/man8/newroot.8                       minix-sys       obsolete
 ./usr/man/man8/nologin.8                       minix-sys
 ./usr/man/man8/nonamed.8                       minix-sys
 ./usr/sbin/installboot_nbsd            minix-sys
 ./usr/sbin/kernel                      minix-sys
 ./usr/sbin/link                                minix-sys
+./usr/sbin/makefs                      minix-sys
 ./usr/sbin/mkfs.mfsv3                  minix-sys
 ./usr/sbin/mkproto                     minix-sys
 ./usr/sbin/mtree                       minix-sys
index ed2ae4306ced0404b55998009dd18202208f83aa..9af12675d311ae3105646b274609896c60dcba4b 100644 (file)
@@ -15,7 +15,7 @@ SUBDIR= \
        shutdown \
 
 # support for various file systems
-SUBDIR+= newfs_ext2fs fsck_ext2fs
+SUBDIR+= newfs_ext2fs fsck_ext2fs newfs_msdos newfs_udf newfs_v7fs
 .if !defined(__MINIX)
 SUBDIR+= newfs fsck_ffs fsdb dump restore clri tunefs resize_ffs
 SUBDIR+= newfs_lfs fsck_lfs dump_lfs resize_lfs
diff --git a/sbin/mount/fattr.c b/sbin/mount/fattr.c
new file mode 100644 (file)
index 0000000..aabb560
--- /dev/null
@@ -0,0 +1,89 @@
+/* $NetBSD: fattr.c,v 1.10 2009/06/19 12:55:45 stacktic Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: fattr.c,v 1.10 2009/06/19 12:55:45 stacktic Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mountprog.h"
+
+int
+a_num(const char *s, const char *id_type)
+{
+       int id;
+       char *ep;
+
+       id = strtol(s, &ep, 0);
+       if (*ep || s == ep || id < 0)
+               errx(1, "unknown %s id: %s", id_type, s);
+       return id;
+}
+
+gid_t
+a_gid(const char *s)
+{
+       struct group *gr;
+
+       if ((gr = getgrnam(s)) != NULL)
+               return gr->gr_gid;
+       return a_num(s, "group");
+}
+
+uid_t
+a_uid(const char *s)
+{
+       struct passwd *pw;
+
+       if ((pw = getpwnam(s)) != NULL)
+               return pw->pw_uid;
+       return a_num(s, "user");
+}
+
+mode_t
+a_mask(const char *s)
+{
+       int rv;
+       char *ep;
+
+       rv = strtol(s, &ep, 8);
+       if (s == ep || *ep || rv < 0)
+               errx(1, "invalid file mode: %s", s);
+       return rv;
+}
diff --git a/sbin/mount/mountprog.h b/sbin/mount/mountprog.h
new file mode 100644 (file)
index 0000000..2c73274
--- /dev/null
@@ -0,0 +1,37 @@
+/*     $NetBSD: mountprog.h,v 1.1 2008/08/05 20:57:45 pooka Exp $      */
+
+/*-
+ * Copyright (c) 2000, 2008 The NetBSD Foundation, Inc.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+int            a_num(const char *, const char *);
+gid_t          a_gid(const char *);
+uid_t          a_uid(const char *);
+mode_t         a_mask(const char *);
+
+int            checkvfsname(const char *, const char **);
+const char **  makevfslist(const char *);
+
+void           pathadj(const char *, char *);
diff --git a/sbin/newfs_msdos/Makefile b/sbin/newfs_msdos/Makefile
new file mode 100644 (file)
index 0000000..1a6dfbf
--- /dev/null
@@ -0,0 +1,25 @@
+# $NetBSD: Makefile,v 1.6 2013/01/21 20:28:38 christos Exp $
+# From: $FreeBSD: src/sbin/newfs_msdos/Makefile,v 1.5 2001/03/26 14:33:18 ru Exp $
+
+.include <bsd.own.mk>
+
+PROG=  newfs_msdos
+MAN=   newfs_msdos.8
+SRCS=  newfs_msdos.c partutil.c mkfs_msdos.c
+
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+LDADD+=-lprop
+DPADD+=${LIBPROP}
+
+FSCK=${NETBSDSRCDIR}/sbin/fsck
+CPPFLAGS+=-I${.CURDIR} -I${FSCK}
+.PATH:  ${FSCK}
+
+
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sbin/newfs_msdos/mkfs_msdos.c b/sbin/newfs_msdos/mkfs_msdos.c
new file mode 100644 (file)
index 0000000..dc7dde3
--- /dev/null
@@ -0,0 +1,976 @@
+/*     $NetBSD: mkfs_msdos.c,v 1.8 2013/10/19 01:09:59 christos Exp $  */
+
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char rcsid[] =
+  "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.15 2000/10/10 01:49:37 wollman Exp $";
+#else
+__RCSID("$NetBSD: mkfs_msdos.c,v 1.8 2013/10/19 01:09:59 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#ifndef MAKEFS
+#include <sys/mount.h>
+#include <sys/disk.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <util.h>
+#include <disktab.h>
+
+#ifndef MAKEFS
+#include "partutil.h"
+#endif
+#include "mkfs_msdos.h"
+
+#define MAXU16   0xffff        /* maximum unsigned 16-bit quantity */
+#define BPN      4             /* bits per nibble */
+#define NPB      2             /* nibbles per byte */
+
+#define DOSMAGIC  0xaa55       /* DOS magic number */
+#define MINBPS   512           /* minimum bytes per sector */
+#define MAXSPC   128           /* maximum sectors per cluster */
+#define MAXNFT   16            /* maximum number of FATs */
+#define DEFBLK   4096          /* default block size */
+#define DEFBLK16  2048         /* default block size FAT16 */
+#define DEFRDE   512           /* default root directory entries */
+#define RESFTE   2             /* reserved FAT entries */
+#define MINCLS12  1            /* minimum FAT12 clusters */
+#define MINCLS16  0xff5                /* minimum FAT16 clusters */
+#define MINCLS32  0xfff5       /* minimum FAT32 clusters */
+#define MAXCLS12  0xff4        /* maximum FAT12 clusters */
+#define MAXCLS16  0xfff4       /* maximum FAT16 clusters */
+#define MAXCLS32  0xffffff4    /* maximum FAT32 clusters */
+
+#define mincls(fat_type)  ((fat_type) == 12 ? MINCLS12 :       \
+                     (fat_type) == 16 ? MINCLS16 :     \
+                                   MINCLS32)
+
+#define maxcls(fat_type)  ((fat_type) == 12 ? MAXCLS12 :       \
+                     (fat_type) == 16 ? MAXCLS16 :     \
+                                   MAXCLS32)
+
+#define mk1(p, x)                              \
+    (p) = (u_int8_t)(x)
+
+#define mk2(p, x)                              \
+    (p)[0] = (u_int8_t)(x),                    \
+    (p)[1] = (u_int8_t)((x) >> 010)
+
+#define mk4(p, x)                              \
+    (p)[0] = (u_int8_t)(x),                    \
+    (p)[1] = (u_int8_t)((x) >> 010),           \
+    (p)[2] = (u_int8_t)((x) >> 020),           \
+    (p)[3] = (u_int8_t)((x) >> 030)
+
+struct bs {
+    u_int8_t jmp[3];           /* bootstrap entry point */
+    u_int8_t oem[8];           /* OEM name and version */
+};
+
+struct bsbpb {
+    u_int8_t bps[2];           /* bytes per sector */
+    u_int8_t spc;              /* sectors per cluster */
+    u_int8_t res[2];           /* reserved sectors */
+    u_int8_t nft;              /* number of FATs */
+    u_int8_t rde[2];           /* root directory entries */
+    u_int8_t sec[2];           /* total sectors */
+    u_int8_t mid;              /* media descriptor */
+    u_int8_t spf[2];           /* sectors per FAT */
+    u_int8_t spt[2];           /* sectors per track */
+    u_int8_t hds[2];           /* drive heads */
+    u_int8_t hid[4];           /* hidden sectors */
+    u_int8_t bsec[4];          /* big total sectors */
+};
+
+struct bsxbpb {
+    u_int8_t bspf[4];          /* big sectors per FAT */
+    u_int8_t xflg[2];          /* FAT control flags */
+    u_int8_t vers[2];          /* file system version */
+    u_int8_t rdcl[4];          /* root directory start cluster */
+    u_int8_t infs[2];          /* file system info sector */
+    u_int8_t bkbs[2];          /* backup boot sector */
+    u_int8_t rsvd[12];         /* reserved */
+};
+
+struct bsx {
+    u_int8_t drv;              /* drive number */
+    u_int8_t rsvd;             /* reserved */
+    u_int8_t sig;              /* extended boot signature */
+    u_int8_t volid[4];         /* volume ID number */
+    u_int8_t label[11];        /* volume label */
+    u_int8_t type[8];          /* file system type */
+};
+
+struct de {
+    u_int8_t namext[11];       /* name and extension */
+    u_int8_t attr;             /* attributes */
+    u_int8_t rsvd[10];         /* reserved */
+    u_int8_t time[2];          /* creation time */
+    u_int8_t date[2];          /* creation date */
+    u_int8_t clus[2];          /* starting cluster */
+    u_int8_t size[4];          /* size */
+};
+
+struct bpb {
+    u_int bps;                 /* bytes per sector */
+    u_int spc;                 /* sectors per cluster */
+    u_int res;                 /* reserved sectors */
+    u_int nft;                 /* number of FATs */
+    u_int rde;                 /* root directory entries */
+    u_int sec;                 /* total sectors */
+    u_int mid;                 /* media descriptor */
+    u_int spf;                 /* sectors per FAT */
+    u_int spt;                 /* sectors per track */
+    u_int hds;                 /* drive heads */
+    u_int hid;                 /* hidden sectors */
+    u_int bsec;                /* big total sectors */
+    u_int bspf;                /* big sectors per FAT */
+    u_int rdcl;                /* root directory start cluster */
+    u_int infs;                /* file system info sector */
+    u_int bkbs;                /* backup boot sector */
+};
+
+#define INIT(a, b, c, d, e, f, g, h, i, j) \
+    { .bps = a, .spc = b, .res = c, .nft = d, .rde = e, \
+      .sec = f, .mid = g, .spf = h, .spt = i, .hds = j, }
+static struct {
+    const char *name;
+    struct bpb bpb;
+} stdfmt[] = {
+    {"160",  INIT(512, 1, 1, 2,  64,  320, 0xfe, 1,  8, 1)},
+    {"180",  INIT(512, 1, 1, 2,  64,  360, 0xfc, 2,  9, 1)},
+    {"320",  INIT(512, 2, 1, 2, 112,  640, 0xff, 1,  8, 2)},
+    {"360",  INIT(512, 2, 1, 2, 112,  720, 0xfd, 2,  9, 2)},
+    {"640",  INIT(512, 2, 1, 2, 112, 1280, 0xfb, 2,  8, 2)},    
+    {"720",  INIT(512, 2, 1, 2, 112, 1440, 0xf9, 3,  9, 2)},
+    {"1200", INIT(512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2)},
+    {"1232", INIT(1024,1, 1, 2, 192, 1232, 0xfe, 2,  8, 2)},    
+    {"1440", INIT(512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2)},
+    {"2880", INIT(512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2)}
+};
+
+static u_int8_t bootcode[] = {
+    0xfa,                      /* cli              */
+    0x31, 0xc0,                /* xor     ax,ax    */
+    0x8e, 0xd0,                /* mov     ss,ax    */
+    0xbc, 0x00, 0x7c,          /* mov     sp,7c00h */
+    0xfb,                      /* sti              */
+    0x8e, 0xd8,                /* mov     ds,ax    */
+    0xe8, 0x00, 0x00,          /* call    $ + 3    */
+    0x5e,                      /* pop     si       */
+    0x83, 0xc6, 0x19,          /* add     si,+19h  */
+    0xbb, 0x07, 0x00,          /* mov     bx,0007h */
+    0xfc,                      /* cld              */
+    0xac,                      /* lodsb            */
+    0x84, 0xc0,                /* test    al,al    */
+    0x74, 0x06,                /* jz      $ + 8    */
+    0xb4, 0x0e,                /* mov     ah,0eh   */
+    0xcd, 0x10,                /* int     10h      */
+    0xeb, 0xf5,                /* jmp     $ - 9    */
+    0x30, 0xe4,                /* xor     ah,ah    */
+    0xcd, 0x16,                /* int     16h      */
+    0xcd, 0x19,                /* int     19h      */
+    0x0d, 0x0a,
+    'N', 'o', 'n', '-', 's', 'y', 's', 't',
+    'e', 'm', ' ', 'd', 'i', 's', 'k',
+    0x0d, 0x0a,
+    'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
+    'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
+    ' ', 'r', 'e', 'b', 'o', 'o', 't',
+    0x0d, 0x0a,
+    0
+};
+
+static int got_siginfo = 0; /* received a SIGINFO */
+
+#ifndef MAKEFS
+static int check_mounted(const char *, mode_t);
+#endif
+static int getstdfmt(const char *, struct bpb *);
+static int getbpbinfo(int, const char *, const char *, int, struct bpb *, int);
+static void print_bpb(struct bpb *);
+static int ckgeom(const char *, u_int, const char *);
+static int oklabel(const char *);
+static void mklabel(u_int8_t *, const char *);
+static void setstr(u_int8_t *, const char *, size_t);
+static void infohandler(int sig);
+
+int
+mkfs_msdos(const char *fname, const char *dtype, const struct msdos_options *op)
+{
+    char buf[MAXPATHLEN];
+    struct stat sb;
+    struct timeval tv;
+    struct bpb bpb;
+    struct tm *tm;
+    struct bs *bs;
+    struct bsbpb *bsbpb;
+    struct bsxbpb *bsxbpb;
+    struct bsx *bsx;
+    struct de *de;
+    u_int8_t *img;
+    const char *bname;
+    ssize_t n;
+    time_t now;
+    u_int bss, rds, cls, dir, lsn, x, x1, x2;
+    int ch, fd, fd1;
+    struct msdos_options o = *op;
+    int oflags = O_RDWR | O_CREAT;
+
+    if (o.block_size && o.sectors_per_cluster) {
+       warnx("Cannot specify both block size and sectors per cluster");
+       return -1;
+    }
+    if (o.OEM_string && strlen(o.OEM_string) > 8) {
+       warnx("%s: bad OEM string", o.OEM_string);
+       return -1;
+    }
+    if (o.create_size) {
+       if (o.no_create) {
+           warnx("create (-C) is incompatible with -N");
+           return -1;
+       }
+       if (o.offset == 0)
+               oflags |= O_TRUNC;
+       fd = open(fname, oflags, 0644);
+       if (fd == -1) {
+           warnx("failed to create %s", fname);
+           return -1;
+       }
+       (void)lseek(fd, o.create_size - 1, SEEK_SET);
+       if (write(fd, "\0", 1) != 1) {
+           warn("failed to set file size");
+           return -1;
+       }
+       (void)lseek(fd, 0, SEEK_SET);
+    } else if ((fd = open(fname, o.no_create ? O_RDONLY : O_RDWR)) == -1 ||
+       fstat(fd, &sb)) {
+       warn("%s", fname);
+       return -1;
+    }
+#ifndef MAKEFS
+    if (!o.no_create)
+       if (check_mounted(fname, sb.st_mode) == -1)
+           return -1;
+#endif
+    if (!S_ISCHR(sb.st_mode) && !o.create_size) {
+       warnx("warning, %s is not a character device", fname);
+       return -1;
+    }
+    if (o.offset && o.offset != lseek(fd, o.offset, SEEK_SET)) {
+       warnx("cannot seek to %jd", (intmax_t)o.offset);
+       return -1;
+    }
+    memset(&bpb, 0, sizeof(bpb));
+    if (o.floppy) {
+       if (getstdfmt(o.floppy, &bpb) == -1)
+           return -1;
+       bpb.bsec = bpb.sec;
+       bpb.sec = 0;
+       bpb.bspf = bpb.spf;
+       bpb.spf = 0;
+    }
+    if (o.drive_heads)
+       bpb.hds = o.drive_heads;
+    if (o.sectors_per_track)
+       bpb.spt = o.sectors_per_track;
+    if (o.bytes_per_sector)
+       bpb.bps = o.bytes_per_sector;
+    if (o.size)
+       bpb.bsec = o.size;
+    if (o.hidden_sectors_set)
+       bpb.hid = o.hidden_sectors;
+    if (!(o.floppy || (o.drive_heads && o.sectors_per_track &&
+       o.bytes_per_sector && o.size && o.hidden_sectors_set))) {
+       if (getbpbinfo(fd, fname, dtype, o.hidden_sectors_set, &bpb,
+           o.create_size != 0) == -1)
+           return -1;
+       bpb.bsec -= (o.offset / bpb.bps);
+       if (bpb.spc == 0) {     /* set defaults */
+           if (bpb.bsec <= 6000)       /* about 3MB -> 512 bytes */
+               bpb.spc = 1;
+           else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */
+               bpb.spc = 8;
+           else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */
+               bpb.spc = 16;
+           else if (bpb.bsec <= (1<<21)) /* 1G -> 16k */
+               bpb.spc = 32;
+           else
+               bpb.spc = 64;           /* otherwise 32k */
+       }
+    }
+
+    if (o.volume_label && !oklabel(o.volume_label)) {
+       warnx("%s: bad volume label", o.volume_label);
+       return -1;
+    }
+
+    switch (o.fat_type) {
+    case 0:
+       if (o.floppy)
+           o.fat_type = 12;
+       else if (!o.directory_entries && (o.info_sector || o.backup_sector))
+           o.fat_type = 32;
+       break;
+    case 12:
+    case 16:
+       if (o.info_sector) {
+           warnx("Cannot specify info sector with FAT%u", o.fat_type);
+           return -1;
+       }
+       if (o.backup_sector) {
+           warnx("Cannot specify backup sector with FAT%u", o.fat_type);
+           return -1;
+       }
+       break;
+    case 32:
+       if (o.directory_entries) {
+           warnx("Cannot specify directory entries with FAT32");
+           return -1;
+       }
+       break;
+    default:
+       warnx("%d: bad FAT type", o.fat_type);
+       return -1;
+    }
+    if (!powerof2(bpb.bps)) {
+       warnx("bytes/sector (%u) is not a power of 2", bpb.bps);
+       return -1;
+    }
+    if (bpb.bps < MINBPS) {
+       warnx("bytes/sector (%u) is too small; minimum is %u",
+            bpb.bps, MINBPS);
+       return -1;
+    }
+
+    if (o.floppy && o.fat_type == 32)
+       bpb.rde = 0;
+    if (o.block_size) {
+       if (!powerof2(o.block_size)) {
+           warnx("block size (%u) is not a power of 2", o.block_size);
+           return -1;
+       }
+       if (o.block_size < bpb.bps) {
+           warnx("block size (%u) is too small; minimum is %u",
+               o.block_size, bpb.bps);
+           return -1;
+       }
+       if (o.block_size > bpb.bps * MAXSPC) {
+           warnx("block size (%u) is too large; maximum is %u",
+               o.block_size, bpb.bps * MAXSPC);
+           return -1;
+       }
+       bpb.spc = o.block_size / bpb.bps;
+    }
+    if (o.sectors_per_cluster) {
+       if (!powerof2(o.sectors_per_cluster)) {
+           warnx("sectors/cluster (%u) is not a power of 2",
+               o.sectors_per_cluster);
+           return -1;
+       }
+       bpb.spc = o.sectors_per_cluster;
+    }
+    if (o.reserved_sectors)
+       bpb.res = o.reserved_sectors;
+    if (o.num_FAT) {
+       if (o.num_FAT > MAXNFT) {
+           warnx("number of FATs (%u) is too large; maximum is %u",
+                o.num_FAT, MAXNFT);
+           return -1;
+       }
+       bpb.nft = o.num_FAT;
+    }
+    if (o.directory_entries)
+       bpb.rde = o.directory_entries;
+    if (o.media_descriptor_set) {
+       if (o.media_descriptor < 0xf0) {
+           warnx("illegal media descriptor (%#x)", o.media_descriptor);
+           return -1;
+       }
+       bpb.mid = o.media_descriptor;
+    }
+    if (o.sectors_per_fat)
+       bpb.bspf = o.sectors_per_fat;
+    if (o.info_sector)
+       bpb.infs = o.info_sector;
+    if (o.backup_sector)
+       bpb.bkbs = o.backup_sector;
+    bss = 1;
+    bname = NULL;
+    fd1 = -1;
+    if (o.bootstrap) {
+       bname = o.bootstrap;
+       if (!strchr(bname, '/')) {
+           snprintf(buf, sizeof(buf), "/boot/%s", bname);
+           if (!(bname = strdup(buf))) {
+               warn(NULL);
+               return -1;
+           }
+       }
+       if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) {
+           warn("%s", bname);
+           return -1;
+       }
+       if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
+           sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16) {
+           warnx("%s: inappropriate file type or format", bname);
+           return -1;
+       }
+       bss = sb.st_size / bpb.bps;
+    }
+    if (!bpb.nft)
+       bpb.nft = 2;
+    if (!o.fat_type) {
+       if (bpb.bsec < (bpb.res ? bpb.res : bss) +
+           howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
+                   ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
+           bpb.nft +
+           howmany(bpb.rde ? bpb.rde : DEFRDE,
+                   bpb.bps / sizeof(struct de)) +
+           (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
+           (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
+           o.fat_type = 12;
+       else if (bpb.rde || bpb.bsec <
+                (bpb.res ? bpb.res : bss) +
+                howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
+                howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
+                (MAXCLS16 + 1) *
+                (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
+           o.fat_type = 16;
+       else
+           o.fat_type = 32;
+    }
+    x = bss;
+    if (o.fat_type == 32) {
+       if (!bpb.infs) {
+           if (x == MAXU16 || x == bpb.bkbs) {
+               warnx("no room for info sector");
+               return -1;
+           }
+           bpb.infs = x;
+       }
+       if (bpb.infs != MAXU16 && x <= bpb.infs)
+           x = bpb.infs + 1;
+       if (!bpb.bkbs) {
+           if (x == MAXU16) {
+               warnx("no room for backup sector");
+               return -1;
+           }
+           bpb.bkbs = x;
+       } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs) {
+           warnx("backup sector would overwrite info sector");
+           return -1;
+       }
+       if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
+           x = bpb.bkbs + 1;
+    }
+    if (!bpb.res)
+       bpb.res = o.fat_type == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x;
+    else if (bpb.res < x) {
+       warnx("too few reserved sectors (need %d have %d)", x, bpb.res);
+       return -1;
+    }
+    if (o.fat_type != 32 && !bpb.rde)
+       bpb.rde = DEFRDE;
+    rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
+    if (!bpb.spc)
+       for (bpb.spc = howmany(o.fat_type == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
+            bpb.spc < MAXSPC &&
+            bpb.res +
+            howmany((RESFTE + maxcls(o.fat_type)) * (o.fat_type / BPN),
+                    bpb.bps * NPB) * bpb.nft +
+            rds +
+            (u_int64_t)(maxcls(o.fat_type) + 1) * bpb.spc <= bpb.bsec;
+            bpb.spc <<= 1);
+    if (o.fat_type != 32 && bpb.bspf > MAXU16) {
+       warnx("too many sectors/FAT for FAT12/16");
+       return -1;
+    }
+    x1 = bpb.res + rds;
+    x = bpb.bspf ? bpb.bspf : 1;
+    if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec) {
+       warnx("meta data exceeds file system size");
+       return -1;
+    }
+    x1 += x * bpb.nft;
+    x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
+       (bpb.spc * bpb.bps * NPB + o.fat_type / BPN * bpb.nft);
+    x2 = howmany((RESFTE + MIN(x, maxcls(o.fat_type))) * (o.fat_type / BPN),
+                bpb.bps * NPB);
+    if (!bpb.bspf) {
+       bpb.bspf = x2;
+       x1 += (bpb.bspf - 1) * bpb.nft;
+    }
+    cls = (bpb.bsec - x1) / bpb.spc;
+    x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (o.fat_type / BPN) - RESFTE;
+    if (cls > x)
+       cls = x;
+    if (bpb.bspf < x2) {
+       warnx("warning: sectors/FAT limits file system to %u clusters",
+             cls);
+       return -1;
+    }
+    if (cls < mincls(o.fat_type)) {
+       warnx("%u clusters too few clusters for FAT%u, need %u", cls,
+           o.fat_type, mincls(o.fat_type));
+       return -1;
+    }
+    if (cls > maxcls(o.fat_type)) {
+       cls = maxcls(o.fat_type);
+       bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
+       warnx("warning: FAT type limits file system to %u sectors",
+             bpb.bsec);
+       return -1;
+    }
+    printf("%s: %u sector%s in %u FAT%u cluster%s "
+          "(%u bytes/cluster)\n", fname, cls * bpb.spc,
+          cls * bpb.spc == 1 ? "" : "s", cls, o.fat_type,
+          cls == 1 ? "" : "s", bpb.bps * bpb.spc);
+    if (!bpb.mid)
+       bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
+    if (o.fat_type == 32)
+       bpb.rdcl = RESFTE;
+    if (bpb.hid + bpb.bsec <= MAXU16) {
+       bpb.sec = bpb.bsec;
+       bpb.bsec = 0;
+    }
+    if (o.fat_type != 32) {
+       bpb.spf = bpb.bspf;
+       bpb.bspf = 0;
+    }
+    ch = 0;
+    if (o.fat_type == 12)
+       ch = 1;                 /* 001 Primary DOS with 12 bit FAT */
+    else if (o.fat_type == 16) {
+       if (bpb.bsec == 0)
+           ch = 4;             /* 004 Primary DOS with 16 bit FAT <32M */
+       else
+           ch = 6;             /* 006 Primary 'big' DOS, 16-bit FAT (> 32MB) */
+                               /*
+                                * XXX: what about:
+                                * 014 DOS (16-bit FAT) - LBA
+                                *  ?
+                                */
+    } else if (o.fat_type == 32) {
+       ch = 11;                /* 011 Primary DOS with 32 bit FAT */
+                               /*
+                                * XXX: what about:
+                                * 012 Primary DOS with 32 bit FAT - LBA
+                                *  ?
+                                */
+    }
+    if (ch != 0)
+       printf("MBR type: %d\n", ch);
+    print_bpb(&bpb);
+    if (!o.no_create) {
+       gettimeofday(&tv, NULL);
+       now = tv.tv_sec;
+       tm = localtime(&now);
+       if (!(img = malloc(bpb.bps)))
+           err(1, NULL);
+       dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
+#ifdef SIGINFO
+       signal(SIGINFO, infohandler);
+#endif
+       for (lsn = 0; lsn < dir + (o.fat_type == 32 ? bpb.spc : rds); lsn++) {
+           if (got_siginfo) {
+               fprintf(stderr,"%s: writing sector %u of %u (%u%%)\n",
+                   fname,lsn,(dir + (o.fat_type == 32 ? bpb.spc : rds)),
+                   (lsn*100)/(dir + (o.fat_type == 32 ? bpb.spc : rds)));
+               got_siginfo = 0;
+           }
+           x = lsn;
+           if (o.bootstrap &&
+               o.fat_type == 32 && bpb.bkbs != MAXU16 &&
+               bss <= bpb.bkbs && x >= bpb.bkbs) {
+               x -= bpb.bkbs;
+               if (!x && lseek(fd1, o.offset, SEEK_SET)) {
+                   warn("%s", bname);
+                   return -1;
+               }
+           }
+           if (o.bootstrap && x < bss) {
+               if ((n = read(fd1, img, bpb.bps)) == -1) {
+                   warn("%s", bname);
+                   return -1;
+               }
+               if ((size_t)n != bpb.bps) {
+                   warnx("%s: can't read sector %u", bname, x);
+                   return -1;
+               }
+           } else
+               memset(img, 0, bpb.bps);
+           if (!lsn ||
+             (o.fat_type == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
+               x1 = sizeof(struct bs);
+               bsbpb = (struct bsbpb *)(img + x1);
+               mk2(bsbpb->bps, bpb.bps);
+               mk1(bsbpb->spc, bpb.spc);
+               mk2(bsbpb->res, bpb.res);
+               mk1(bsbpb->nft, bpb.nft);
+               mk2(bsbpb->rde, bpb.rde);
+               mk2(bsbpb->sec, bpb.sec);
+               mk1(bsbpb->mid, bpb.mid);
+               mk2(bsbpb->spf, bpb.spf);
+               mk2(bsbpb->spt, bpb.spt);
+               mk2(bsbpb->hds, bpb.hds);
+               mk4(bsbpb->hid, bpb.hid);
+               mk4(bsbpb->bsec, bpb.bsec);
+               x1 += sizeof(struct bsbpb);
+               if (o.fat_type == 32) {
+                   bsxbpb = (struct bsxbpb *)(img + x1);
+                   mk4(bsxbpb->bspf, bpb.bspf);
+                   mk2(bsxbpb->xflg, 0);
+                   mk2(bsxbpb->vers, 0);
+                   mk4(bsxbpb->rdcl, bpb.rdcl);
+                   mk2(bsxbpb->infs, bpb.infs);
+                   mk2(bsxbpb->bkbs, bpb.bkbs);
+                   x1 += sizeof(struct bsxbpb);
+               }
+               bsx = (struct bsx *)(img + x1);
+               mk1(bsx->sig, 0x29);
+               if (o.volume_id_set)
+                   x = o.volume_id;
+               else
+                   x = (((u_int)(1 + tm->tm_mon) << 8 |
+                         (u_int)tm->tm_mday) +
+                        ((u_int)tm->tm_sec << 8 |
+                         (u_int)(tv.tv_usec / 10))) << 16 |
+                       ((u_int)(1900 + tm->tm_year) +
+                        ((u_int)tm->tm_hour << 8 |
+                         (u_int)tm->tm_min));
+               mk4(bsx->volid, x);
+               mklabel(bsx->label, o.volume_label ? o.volume_label : "NO NAME");
+               snprintf(buf, sizeof(buf), "FAT%u", o.fat_type);
+               setstr(bsx->type, buf, sizeof(bsx->type));
+               if (!o.bootstrap) {
+                   x1 += sizeof(struct bsx);
+                   bs = (struct bs *)img;
+                   mk1(bs->jmp[0], 0xeb);
+                   mk1(bs->jmp[1], x1 - 2);
+                   mk1(bs->jmp[2], 0x90);
+                   setstr(bs->oem, o.OEM_string ? o.OEM_string : "NetBSD",
+                          sizeof(bs->oem));
+                   memcpy(img + x1, bootcode, sizeof(bootcode));
+                   mk2(img + MINBPS - 2, DOSMAGIC);
+               }
+           } else if (o.fat_type == 32 && bpb.infs != MAXU16 &&
+                      (lsn == bpb.infs ||
+                       (bpb.bkbs != MAXU16 &&
+                        lsn == bpb.bkbs + bpb.infs))) {
+               mk4(img, 0x41615252);
+               mk4(img + MINBPS - 28, 0x61417272);
+               mk4(img + MINBPS - 24, 0xffffffff);
+               mk4(img + MINBPS - 20, bpb.rdcl);
+               mk2(img + MINBPS - 2, DOSMAGIC);
+           } else if (lsn >= bpb.res && lsn < dir &&
+                      !((lsn - bpb.res) %
+                        (bpb.spf ? bpb.spf : bpb.bspf))) {
+               mk1(img[0], bpb.mid);
+               for (x = 1; x < o.fat_type * (o.fat_type == 32 ? 3U : 2U) / 8U; x++)
+                   mk1(img[x], o.fat_type == 32 && x % 4 == 3 ? 0x0f : 0xff);
+           } else if (lsn == dir && o.volume_label) {
+               de = (struct de *)img;
+               mklabel(de->namext, o.volume_label);
+               mk1(de->attr, 050);
+               x = (u_int)tm->tm_hour << 11 |
+                   (u_int)tm->tm_min << 5 |
+                   (u_int)tm->tm_sec >> 1;
+               mk2(de->time, x);
+               x = (u_int)(tm->tm_year - 80) << 9 |
+                   (u_int)(tm->tm_mon + 1) << 5 |
+                   (u_int)tm->tm_mday;
+               mk2(de->date, x);
+           }
+           if ((n = write(fd, img, bpb.bps)) == -1) {
+               warn("%s", fname);
+               return -1;
+           }
+           if ((size_t)n != bpb.bps) {
+               warnx("%s: can't write sector %u", fname, lsn);
+               return -1;
+           }
+       }
+    }
+    return 0;
+}
+
+#ifndef MAKEFS
+/*
+ * return -1 with error if file system is mounted.
+ */
+static int
+check_mounted(const char *fname, mode_t mode)
+{
+    struct statvfs *mp;
+    const char *s1, *s2;
+    size_t len;
+    int n, r;
+
+    if (!(n = getmntinfo(&mp, MNT_NOWAIT))) {
+       warn("getmntinfo");
+       return -1;
+    }
+    len = strlen(_PATH_DEV);
+    s1 = fname;
+    if (!strncmp(s1, _PATH_DEV, len))
+       s1 += len;
+    r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
+    for (; n--; mp++) {
+       s2 = mp->f_mntfromname;
+       if (!strncmp(s2, _PATH_DEV, len))
+           s2 += len;
+       if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) ||
+           !strcmp(s1, s2)) {
+           warnx("%s is mounted on %s", fname, mp->f_mntonname);
+           return -1;
+       }
+    }
+    return 0;
+}
+#endif
+
+/*
+ * Get a standard format.
+ */
+static int
+getstdfmt(const char *fmt, struct bpb *bpb)
+{
+    u_int x, i;
+
+    x = sizeof(stdfmt) / sizeof(stdfmt[0]);
+    for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
+    if (i == x) {
+       warnx("%s: unknown standard format", fmt);
+       return -1;
+    }
+    *bpb = stdfmt[i].bpb;
+    return 0;
+}
+
+/*
+ * Get disk slice, partition, and geometry information.
+ */
+static int
+getbpbinfo(int fd, const char *fname, const char *dtype, int iflag,
+    struct bpb *bpb, int create)
+{
+    const char *s1, *s2;
+    int part;
+
+    part = -1;
+    s1 = fname;
+    if ((s2 = strrchr(s1, '/')))
+       s1 = s2 + 1;
+    for (s2 = s1; *s2 && !isdigit((unsigned char)*s2); s2++);
+    if (!*s2 || s2 == s1)
+       s2 = NULL;
+    else
+       while (isdigit((unsigned char)*++s2));
+    s1 = s2;
+
+#ifndef MAKEFS
+    int maxpartitions = getmaxpartitions();
+
+    // XXX: Does not work with wedges
+    if (s2 && *s2 >= 'a' && *s2 <= 'a' + maxpartitions - 1) {
+       part = *s2++ - 'a';
+    }
+#endif
+    if (((part != -1) && ((!iflag && part != -1) || !bpb->bsec)) ||
+       !bpb->bps || !bpb->spt || !bpb->hds) {
+       u_int sector_size;
+       u_int nsectors;
+       u_int ntracks;
+       u_int size;
+#ifndef MAKEFS
+       struct disk_geom geo;
+       struct dkwedge_info dkw;
+
+       if (!create && getdiskinfo(fname, fd, NULL, &geo, &dkw) != -1) {
+           sector_size = geo.dg_secsize = 512;
+           nsectors = geo.dg_nsectors = 63;
+           ntracks = geo.dg_ntracks = 255;
+           size = dkw.dkw_size;
+       } else
+#endif
+       {
+           struct stat st;
+
+           if (fstat(fd, &st) == -1) {
+               warnx("Can't get disk size for `%s'", fname);
+               return -1;
+           }
+           /* create a fake geometry for a file image */
+           sector_size = 512;
+           nsectors = 63;
+           ntracks = 255;
+           size = st.st_size / sector_size;
+       }
+       if (!bpb->bps) {
+           if (ckgeom(fname, sector_size, "bytes/sector") == -1)
+               return -1;
+           bpb->bps = sector_size;
+       }
+
+       if (nsectors > 63) {
+               /*
+                * The kernel doesn't accept BPB with spt > 63.
+                * (see sys/fs/msdosfs/msdosfs_vfsops.c:msdosfs_mountfs())
+                * If values taken from disklabel don't match these
+                * restrictions, use popular BIOS default values instead.
+                */
+               nsectors = 63;
+       }
+       if (!bpb->spt) {
+           if (ckgeom(fname, nsectors, "sectors/track") == -1)
+               return -1;
+           bpb->spt = nsectors;
+       }
+       if (!bpb->hds)
+           if (ckgeom(fname, ntracks, "drive heads") == -1)
+               return -1;
+           bpb->hds = ntracks;
+       if (!bpb->bsec)
+           bpb->bsec = size;
+    }
+    return 0;
+}
+
+/*
+ * Print out BPB values.
+ */
+static void
+print_bpb(struct bpb *bpb)
+{
+    printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
+          bpb->nft);
+    if (bpb->rde)
+       printf(" rde=%u", bpb->rde);
+    if (bpb->sec)
+       printf(" sec=%u", bpb->sec);
+    printf(" mid=%#x", bpb->mid);
+    if (bpb->spf)
+       printf(" spf=%u", bpb->spf);
+    printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
+    if (bpb->bsec)
+       printf(" bsec=%u", bpb->bsec);
+    if (!bpb->spf) {
+       printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
+       printf(" infs=");
+       printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
+       printf(" bkbs=");
+       printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
+    }
+    printf("\n");
+}
+
+/*
+ * Check a disk geometry value.
+ */
+static int
+ckgeom(const char *fname, u_int val, const char *msg)
+{
+    if (!val) {
+       warnx("%s: no default %s", fname, msg);
+       return -1;
+    }
+    if (val > MAXU16) {
+       warnx("%s: illegal %s", fname, msg);
+       return -1;
+    }
+    return 0;
+}
+/*
+ * Check a volume label.
+ */
+static int
+oklabel(const char *src)
+{
+    int c, i;
+
+    for (i = 0; i <= 11; i++) {
+       c = (u_char)*src++;
+       if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
+           break;
+    }
+    return i && !c;
+}
+
+/*
+ * Make a volume label.
+ */
+static void
+mklabel(u_int8_t *dest, const char *src)
+{
+    int c, i;
+
+    for (i = 0; i < 11; i++) {
+       c = *src ? toupper((unsigned char)*src++) : ' ';
+       *dest++ = !i && c == '\xe5' ? 5 : c;
+    }
+}
+
+/*
+ * Copy string, padding with spaces.
+ */
+static void
+setstr(u_int8_t *dest, const char *src, size_t len)
+{
+    while (len--)
+       *dest++ = *src ? *src++ : ' ';
+}
+
+static void
+infohandler(int sig)
+{
+    got_siginfo = 1;
+}
diff --git a/sbin/newfs_msdos/mkfs_msdos.h b/sbin/newfs_msdos/mkfs_msdos.h
new file mode 100644 (file)
index 0000000..1987636
--- /dev/null
@@ -0,0 +1,71 @@
+/*     $NetBSD: mkfs_msdos.h,v 1.2 2013/01/23 15:29:15 christos Exp $  */
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/types.h>
+#include <stdbool.h>
+#define ALLOPTS \
+AOPT('@', off_t, offset, 0, "Offset in device") \
+AOPT('B', char *, bootstrap, -1, "Bootstrap file") \
+AOPT('C', off_t, create_size, 0, "Create file") \
+AOPT('F', uint8_t,  fat_type, 12, "FAT type (12, 16, or 32)") \
+AOPT('I', uint32_t, volume_id, 0, "Volume ID") \
+AOPT('L', char *, volume_label, -1, "Volume Label") \
+AOPT('N', bool, no_create, -2, "Don't create filesystem, print params only") \
+AOPT('O', char *, OEM_string, -1, "OEM string") \
+AOPT('S', uint16_t, bytes_per_sector, 1, "Bytes per sector") \
+AOPT('a', uint32_t, sectors_per_fat, 1, "Sectors per FAT") \
+AOPT('b', uint32_t, block_size, 1, "Block size") \
+AOPT('c', uint8_t, sectors_per_cluster, 1, "Sectors per cluster") \
+AOPT('e', uint16_t, directory_entries, 1, "Directory entries") \
+AOPT('f', char *, floppy, -1, "Standard format floppies (160,180,320,360,640,720,1200,1232,1440,2880)") \
+AOPT('h', uint16_t, drive_heads, 1, "Drive heads") \
+AOPT('i', uint16_t, info_sector, 1, "Info sector") \
+AOPT('k', uint16_t, backup_sector, 1, "Backup sector") \
+AOPT('m', uint8_t, media_descriptor, 0, "Media descriptor") \
+AOPT('n', uint8_t, num_FAT, 1, "Number of FATs") \
+AOPT('o', uint32_t, hidden_sectors, 0, "Hidden sectors") \
+AOPT('r', uint16_t, reserved_sectors, 1, "Reserved sectors") \
+AOPT('s', uint32_t, size, 1, "File System size") \
+AOPT('u', uint16_t, sectors_per_track, 1, "Sectors per track")
+
+struct msdos_options {
+#define AOPT(_opt, _type, _name, _min, _desc) _type _name;
+ALLOPTS
+#undef AOPT    
+       uint32_t volume_id_set:1;
+       uint32_t media_descriptor_set:1;
+       uint32_t hidden_sectors_set:1;
+};
+
+int mkfs_msdos(const char *, const char *, const struct msdos_options *);
diff --git a/sbin/newfs_msdos/newfs_msdos.8 b/sbin/newfs_msdos/newfs_msdos.8
new file mode 100644 (file)
index 0000000..930874a
--- /dev/null
@@ -0,0 +1,241 @@
+.\" $NetBSD: newfs_msdos.8,v 1.18 2013/07/20 21:39:58 wiz Exp $
+.\"
+.\" Copyright (c) 1998 Robert Nordier
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
+.\"
+.\" From: $FreeBSD: src/sbin/newfs_msdos/newfs_msdos.8,v 1.13 2001/08/14 10:01:48 ru Exp $
+.\"
+.Dd March 26, 2009
+.Dt NEWFS_MSDOS 8
+.Os
+.Sh NAME
+.Nm newfs_msdos
+.Nd construct a new MS-DOS (FAT) file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl N
+.Op Fl @ Ar offset
+.Op Fl B Ar boot
+.Op Fl C Ar create-size
+.Op Fl F Ar FAT-type
+.Op Fl I Ar volid
+.Op Fl L Ar label
+.Op Fl O Ar OEM
+.Op Fl S Ar sector-size
+.Op Fl a Ar FAT-size
+.Op Fl b Ar block-size
+.Op Fl c Ar cluster-size
+.Op Fl e Ar dirents
+.Op Fl f Ar format
+.Op Fl h Ar heads
+.Op Fl i Ar info
+.Op Fl k Ar backup
+.Op Fl m Ar media
+.Op Fl n Ar FATs
+.Op Fl o Ar hidden
+.Op Fl r Ar reserved
+.Op Fl s Ar total
+.Op Fl u Ar track-size
+.Ar special
+.Op Ar disktype
+.Sh DESCRIPTION
+The
+.Nm
+utility creates a FAT12, FAT16, or FAT32 file system on device or file named
+.Ar special ,
+using
+.Xr disktab 5
+entry
+.Ar disktype
+to determine geometry, if required.
+.Pp
+The options are as follow:
+.Bl -tag -width indent
+.It Fl N
+Don't create a file system: just print out parameters.
+.It Fl @ Ar offset
+Build the filesystem at the specified offset in bytes in the device or file.
+A suffix s, k, m, g (lower or upper case)
+appended to the offset specifies that the
+number is in sectors, kilobytes, megabytes or gigabytes, respectively.
+.It Fl B Ar boot
+Get bootstrap from file.
+.It Fl C Ar create-size
+Create the image file with the specified size.
+A suffix character appended to the size is interpreted as for the
+.Fl @
+option.
+The file is created by truncating any existing file with the
+same name, seeking just before the required size and writing
+a single 0 byte.
+As a consequence, the space occupied on disk
+may be smaller than the size specified as a parameter.
+.It Fl F Ar FAT-type
+FAT type (one of 12, 16, or 32).
+.It Fl I Ar volid
+Volume ID.
+.It Fl L Ar label
+Volume label (up to 11 characters).
+The label should consist of only those characters permitted
+in regular DOS (8+3) filenames.
+The default is
+.Qq Li "NO_NAME" .
+.It Fl O Ar OEM
+OEM string (up to 8 characters).
+The default is
+.Qq Li "NetBSD" .
+.It Fl S Ar sector-size
+Number of bytes per sector.
+Acceptable values are powers of 2 in the range 512 through 32768.
+.It Fl a Ar FAT-size
+Number of sectors per FAT.
+.It Fl b Ar block-size
+File system block size (bytes per cluster).
+This should resolve to an acceptable number of sectors
+per cluster (see below).
+.It Fl c Ar cluster-size
+Sectors per cluster.
+Acceptable values are powers of 2 in the range 1 through 128.
+If the block or cluster size are not specified, the code
+uses a cluster between 512 bytes and 32K depending on
+the filesystem size.
+.It Fl e Ar dirents
+Number of root directory entries (FAT12 and FAT16 only).
+.It Fl f Ar format
+Specify a standard (floppy disk) format.
+The standard formats are (capacities in kilobytes):
+160, 180, 320, 360, 640, 720, 1200, 1232, 1440, 2880.
+.It Fl h Ar heads
+Number of drive heads.
+.It Fl i Ar info
+Location of the file system info sector (FAT32 only).
+A value of 0xffff signifies no info sector.
+.It Fl k Ar backup
+Location of the backup boot sector (FAT32 only).
+A value of 0xffff signifies no backup sector.
+.It Fl m Ar media
+Media descriptor (acceptable range 0xf0 to 0xff).
+.It Fl n Ar FATs
+Number of FATs.
+Acceptable values are 1 to 16 inclusive.
+The default is 2.
+.It Fl o Ar hidden
+Number of hidden sectors.
+.It Fl r Ar reserved
+Number of reserved sectors.
+.It Fl s Ar total
+File system size.
+.It Fl u Ar track-size
+Number of sectors per track.
+.El
+.Pp
+If
+.Nm
+receives a
+.Dv SIGINFO
+signal
+(see the
+.Sy status
+argument for
+.Xr stty 1 ) ,
+a line will be written to the standard error output indicating
+the name of the device currently being formatted, the sector
+number being written, and the total number of sectors to be written.
+.Sh NOTES
+If some parameters (e.g. size, number of sectors, etc.) are not specified
+through options or disktype, the program tries to generate them
+automatically.
+In particular, the size is determined as the
+device or file size minus the offset specified with the
+.Fl @
+option.
+When the geometry is not available, it is assumed to be
+63 sectors, 255 heads.
+The size is then rounded to become
+a multiple of the track size and avoid complaints by some filesystem code.
+.Pp
+FAT file system parameters occupy a "Boot Sector BPB (BIOS Parameter
+Block)" in the first of the "reserved" sectors which precede the actual
+file system.
+For reference purposes, this structure is presented below.
+.Bd -literal
+struct bsbpb {
+    u_int16_t  bps;            /* [-S] bytes per sector */
+    u_int8_t   spc;            /* [-c] sectors per cluster */
+    u_int16_t  res;            /* [-r] reserved sectors */
+    u_int8_t   nft;            /* [-n] number of FATs */
+    u_int16_t  rde;            /* [-e] root directory entries */
+    u_int16_t  sec;            /* [-s] total sectors */
+    u_int8_t   mid;            /* [-m] media descriptor */
+    u_int16_t  spf;            /* [-a] sectors per FAT */
+    u_int16_t  spt;            /* [-u] sectors per track */
+    u_int16_t  hds;            /* [-h] drive heads */
+    u_int32_t  hid;            /* [-o] hidden sectors */
+    u_int32_t  bsec;           /* [-s] big total sectors */
+};
+/* FAT32 extensions */
+struct bsxbpb {
+    u_int32_t  bspf;           /* [-a] big sectors per FAT */
+    u_int16_t  xflg;           /* control flags */
+    u_int16_t  vers;           /* file system version */
+    u_int32_t  rdcl;           /* root directory start cluster */
+    u_int16_t  infs;           /* [-i] file system info sector */
+    u_int16_t  bkbs;           /* [-k] backup boot sector */
+};
+.Ed
+.Sh EXAMPLES
+.Bd -literal -offset indent
+newfs_msdos /dev/rwd1a
+.Ed
+.Pp
+Create a file system, using default parameters, on
+.Pa /dev/rwd1a .
+.Bd -literal -offset indent
+newfs_msdos -f 1440 -L foo /dev/rfd0a
+.Ed
+.Pp
+Create a standard 1.44M file system, with volume label
+.Ar foo ,
+on
+.Pa /dev/rfd0a .
+Create a 30MB image file, with the FAT partition starting
+63 sectors within the image file:
+.Bd -literal -offset indent
+newfs_msdos -C 30M -@63s ./somefile
+.Ed
+.Sh DIAGNOSTICS
+Exit status is 0 on success and 1 on error.
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr disklabel 8 ,
+.Xr fdisk 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Nx 1.3 .
+.Sh AUTHORS
+.An Robert Nordier Aq Mt rnordier@FreeBSD.org .
diff --git a/sbin/newfs_msdos/newfs_msdos.c b/sbin/newfs_msdos/newfs_msdos.c
new file mode 100644 (file)
index 0000000..42b61d1
--- /dev/null
@@ -0,0 +1,253 @@
+/*     $NetBSD: newfs_msdos.c,v 1.42 2013/01/23 15:29:15 christos Exp $        */
+
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char rcsid[] =
+  "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.15 2000/10/10 01:49:37 wollman Exp $";
+#else
+__RCSID("$NetBSD: newfs_msdos.c,v 1.42 2013/01/23 15:29:15 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <paths.h>
+#include <errno.h>
+
+#include "mkfs_msdos.h"
+
+#define argto1(arg, lo, msg)  argtou(arg, lo, 0xff, msg)
+#define argto2(arg, lo, msg)  argtou(arg, lo, 0xffff, msg)
+#define argto4(arg, lo, msg)  argtou(arg, lo, 0xffffffff, msg)
+#define argtox(arg, lo, msg)  argtou(arg, lo, UINT_MAX, msg)
+
+__dead static void usage(void);
+static u_int argtou(const char *, u_int, u_int, const char *);
+static off_t argtooff(const char *, const char *);
+
+/*
+ * Construct a FAT12, FAT16, or FAT32 file system.
+ */
+int
+main(int argc, char *argv[])
+{
+    static const char opts[] = "@:NB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
+    struct msdos_options o;
+    char *fname, *dtype;
+    char buf[MAXPATHLEN];
+    int ch;
+
+    memset(&o, 0, sizeof(o));
+
+    while ((ch = getopt(argc, argv, opts)) != -1)
+       switch (ch) {
+       case '@':
+           o.offset = argtooff(optarg, "offset");
+           break;
+       case 'N':
+           o.no_create = 1;
+           break;
+       case 'B':
+           o.bootstrap = optarg;
+           break;
+       case 'C':
+           o.create_size = argtooff(optarg, "create size");
+           break;
+       case 'F':
+           o.fat_type = atoi(optarg);
+           break;
+       case 'I':
+           o.volume_id = argto4(optarg, 0, "volume ID");
+           o.volume_id_set = 1;
+           break;
+       case 'L':
+           o.volume_label = optarg;
+           break;
+       case 'O':
+           o.OEM_string = optarg;
+           break;
+       case 'S':
+           o.bytes_per_sector = argto2(optarg, 1, "bytes/sector");
+           break;
+       case 'a':
+           o.sectors_per_fat = argto4(optarg, 1, "sectors/FAT");
+           break;
+       case 'b':
+           o.block_size = argtox(optarg, 1, "block size");
+           break;
+       case 'c':
+           o.sectors_per_cluster = argto1(optarg, 1, "sectors/cluster");
+           break;
+       case 'e':
+           o.directory_entries = argto2(optarg, 1, "directory entries");
+           break;
+       case 'f':
+           o.floppy = optarg;
+           break;
+       case 'h':
+           o.drive_heads = argto2(optarg, 1, "drive heads");
+           break;
+       case 'i':
+           o.info_sector = argto2(optarg, 1, "info sector");
+           break;
+       case 'k':
+           o.backup_sector = argto2(optarg, 1, "backup sector");
+           break;
+       case 'm':
+           o.media_descriptor = argto1(optarg, 0, "media descriptor");
+           o.media_descriptor_set = 1;
+           break;
+       case 'n':
+           o.num_FAT = argto1(optarg, 1, "number of FATs");
+           break;
+       case 'o':
+           o.hidden_sectors = argto4(optarg, 0, "hidden sectors");
+           o.hidden_sectors_set = 1;
+           break;
+       case 'r':
+           o.reserved_sectors = argto2(optarg, 1, "reserved sectors");
+           break;
+       case 's':
+           o.size = argto4(optarg, 1, "file system size");
+           break;
+       case 'u':
+           o.sectors_per_track = argto2(optarg, 1, "sectors/track");
+           break;
+       default:
+           usage();
+       }
+    argc -= optind;
+    argv += optind;
+    if (argc < 1 || argc > 2)
+       usage();
+    fname = *argv++;
+    if (!strchr(fname, '/') && !o.create_size) {
+       snprintf(buf, sizeof(buf), "%sr%s", _PATH_DEV, fname);
+       if (!(fname = strdup(buf)))
+           err(1, NULL);
+    }
+    dtype = *argv;
+    return mkfs_msdos(fname, dtype, &o);
+}
+
+/*
+ * Convert and check a numeric option argument.
+ */
+static u_int
+argtou(const char *arg, u_int lo, u_int hi, const char *msg)
+{
+    char *s;
+    u_long x;
+
+    errno = 0;
+    x = strtoul(arg, &s, 0);
+    if (errno || !*arg || *s || x < lo || x > hi)
+       errx(1, "%s: bad %s", arg, msg);
+    return x;
+}
+
+/*
+ * Same for off_t, with optional skmgpP suffix
+ */
+static off_t
+argtooff(const char *arg, const char *msg)
+{
+    char *s;
+    off_t x;
+
+    errno = 0;
+    x = strtoll(arg, &s, 0);
+    /* allow at most one extra char */
+    if (errno || x < 0 || (s[0] && s[1]) )
+       errx(1, "%s: bad %s", arg, msg);
+    if (*s) {  /* the extra char is the multiplier */
+       switch (*s) {
+       default:
+           errx(1, "%s: bad %s", arg, msg);
+           /* notreached */
+       
+       case 's':       /* sector */
+       case 'S':
+           x <<= 9;    /* times 512 */
+           break;
+
+       case 'k':       /* kilobyte */
+       case 'K':
+           x <<= 10;   /* times 1024 */
+           break;
+
+       case 'm':       /* megabyte */
+       case 'M':
+           x <<= 20;   /* times 1024*1024 */
+           break;
+
+       case 'g':       /* gigabyte */
+       case 'G':
+           x <<= 30;   /* times 1024*1024*1024 */
+           break;
+
+       case 'p':       /* partition start */
+       case 'P':       /* partition start */
+       case 'l':       /* partition length */
+       case 'L':       /* partition length */
+           errx(1, "%s: not supported yet %s", arg, msg);
+           return -1;
+           /* notreached */
+       }
+    }
+    return x;
+}
+
+/*
+ * Print usage message.
+ */
+static void
+usage(void)
+{
+    fprintf(stderr,
+           "usage: %s [ -options ] special [disktype]\n", getprogname());
+    fprintf(stderr, "where the options are:\n");
+static struct {
+       char o;
+       const char *h;
+} opts[] = {
+#define AOPT(_opt, _type, _name, _min, _desc) { _opt, _desc },
+ALLOPTS
+#undef AOPT
+};
+    for (size_t i = 0; i < __arraycount(opts); i++)
+       fprintf(stderr, "\t-%c %s\n", opts[i].o, opts[i].h); 
+    exit(1);
+}
diff --git a/sbin/newfs_udf/Makefile b/sbin/newfs_udf/Makefile
new file mode 100644 (file)
index 0000000..008587e
--- /dev/null
@@ -0,0 +1,17 @@
+# $NetBSD: Makefile,v 1.4 2013/07/18 12:44:21 reinoud Exp $
+
+.include <bsd.own.mk>
+
+PROG=  newfs_udf
+MAN=   newfs_udf.8
+SRCS=  newfs_udf.c udf_create.c udf_write.c udf_osta.c fattr.c
+
+MOUNT=  ${NETBSDSRCDIR}/sbin/mount
+KUDF=  ${NETBSDSRCDIR}/sys/fs/udf
+CPPFLAGS+= -I${MOUNT} -I${KUDF} -I${NETBSDSRCDIR}/sys
+.PATH:  ${MOUNT} ${KUDF}
+
+DPADD+=${LIBUTIL}
+LDADD+=-lutil
+
+.include <bsd.prog.mk>
diff --git a/sbin/newfs_udf/newfs_udf.8 b/sbin/newfs_udf/newfs_udf.8
new file mode 100644 (file)
index 0000000..2660fb4
--- /dev/null
@@ -0,0 +1,191 @@
+.\" $NetBSD: newfs_udf.8,v 1.18 2013/08/06 12:15:20 wiz Exp $
+.\"
+.\" Copyright (c) 2008 Reinoud Zandijk
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
+.\"
+.\"
+.Dd August 2, 2013
+.Dt NEWFS_UDF 8
+.Os
+.Sh NAME
+.Nm newfs_udf
+.Nd construct a new UDF file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl cFM
+.Op Fl B Ar blockingsize
+.Op Fl L Ar loglabel
+.Op Fl P Ar discid
+.Op Fl p Ar percentage
+.Op Fl S Ar sectorsize
+.Op Fl s Ar size
+.Op Fl t Ar gmtoff
+.Op Fl V Ar max_udf
+.Op Fl v Ar min_udf
+.Ar special
+.Sh DESCRIPTION
+The
+.Nm
+utility creates an UDF file system on device
+.Ar special
+suitable for the media currently inserted.
+.Pp
+The options are as follow:
+.Bl -tag -width indent
+.It Fl B Ar blockingsize
+When creating image files, specify the blocking size or packetsize of the media
+to
+.Ar blockingsize .
+.It Fl c
+Perform a crude surface check first to weed out disc faults on rewritable
+media.
+.It Fl F
+Force file system construction on non-empty recordable media or create an
+image file.
+.It Fl L Ar loglabel
+Set the disc logical label to the specified
+.Ar loglabel .
+.It Fl M
+Disable metadata partition creation when selected UDF version or media dictates
+this.
+For strict conformance and interchange, don't disable this unless
+its causing problems.
+.It Fl P Ar discid
+Set the physical disc label to the specified
+.Ar discid .
+.Pp
+Prepend
+.Ar discid
+with volsetname separated with a ':' if wanted.
+For strict conformance and interchange, don't set this manually unless it has
+a unique hex number in the first 8 character positions.
+.It Fl p Ar percentage
+Percentage of partition to be initially reserved for metadata on the Metadata
+partition.
+It defaults to 20 %.
+.It Fl S Ar sectorsize
+Set the sectorsize for image files.
+For strict conformance and interchange, don't set this manually.
+.It Fl s Ar size
+For image files, set the file size to the humanized size
+.Ar size .
+.It Fl t Ar gmtoff
+Use the specified
+.Ar gmtoff
+as gmt time offset for recording times on the disc.
+.It Fl V Ar max_udf
+Select
+.Ar max_udf
+as the maximum UDF version to be supported.
+For UDF version 2.50, use
+.Dq 0x250
+or
+.Dq 2.50 .
+.It Fl v Ar min_udf
+Select
+.Ar min_udf
+as the minimum UDF version to be supported.
+For UDF version 2.01, use
+.Dq 0x201
+or
+.Dq 2.01 .
+.El
+.Sh NOTES
+The UDF file system is defined for the entire optical medium.
+It can only function on the entire CD/DVD/BD so the raw partition
+has to be specified for read/write actions.
+For
+.Nm
+this means specifying the raw device with the raw partition, i.e.
+.Pa /dev/rcd0d
+or
+.Pa /dev/rcd0c .
+.Pp
+Some rewritable optical media needs to be formatted first before it can be
+used by UDF.
+This can be done using
+.Xr mmcformat 8 .
+.Pp
+The default UDF version is version 2.01.
+.Sh EXAMPLES
+Create a file system, using the specified names on the device
+.Pa /dev/rcd0d
+with the default UDF version :
+.Bd -literal -offset indent
+newfs_udf -P "Encyclopedia:copy-nr-1" -L "volume 2" /dev/rcd0d
+.Ed
+.Pp
+Create a 4.8 GiB sparse file and configure it using
+.Xr vnconfig 8
+to be a 2048 sector size disc and create a new UDF file system on
+.Pa /dev/rvnd0d
+:
+.Bd -literal -offset indent
+dd if=/dev/zero of=bigdisk.2048.udf seek=9999999 count=1
+vnconfig -c vnd0 bigdisk.2048.udf 2048/1/1/1
+newfs_udf -L bigdisk /dev/rvnd0d
+.Ed
+.Pp
+Create a 2 GiB file and create a new UDF file system on it using the default
+512 byte sector size :
+.Bd -literal -offset indent
+newfs_udf -L bigdisk2 -F -s 2G bigdisk2.iso
+.Ed
+.Pp
+Create a 200 MiB file and create a new UDF file system on it using a sector size
+of 2048 :
+.Bd -literal -offset indent
+newfs_udf -L bigdisk2 -F -s 200M -S 2048 bigdisk3.iso
+.Ed
+.Pp
+Create a new UDF file system on the inserted USB stick using its
+native sectorsize of 512 :
+.Bd -literal -offset indent
+newfs_udf -L "My USB stick" /dev/rsd0d
+.Ed
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr disklabel 8 ,
+.Xr mmcformat 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Nx 5.0 .
+.Sh AUTHORS
+.An Reinoud Zandijk Aq Mt reinoud@NetBSD.org
+.Sh BUGS
+The
+.Ar P
+and the
+.Ar S
+arguments have changed meaning.
+The meaning of
+.Ar S
+has been merged into
+.Ar P
+since
+.Nx 6.1 .
diff --git a/sbin/newfs_udf/newfs_udf.c b/sbin/newfs_udf/newfs_udf.c
new file mode 100644 (file)
index 0000000..eaab2b1
--- /dev/null
@@ -0,0 +1,896 @@
+/* $NetBSD: newfs_udf.c,v 1.18 2013/08/09 15:11:08 reinoud Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+/*
+ * TODO
+ * - implement metadata formatting for BD-R
+ * - implement support for a read-only companion partition?
+ */
+
+#define _EXPOSE_MMC
+#if 0
+# define DEBUG
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <util.h>
+#include <time.h>
+#include <assert.h>
+#include <err.h>
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/cdio.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#include "mountprog.h"
+#include "udf_create.h"
+#include "udf_write.h"
+#include "newfs_udf.h"
+
+/* prototypes */
+int newfs_udf(int argc, char **argv);
+static void usage(void) __attribute__((__noreturn__));
+
+
+/* queue for temporary storage of sectors to be written out */
+struct wrsect {
+       uint64_t  sectornr;
+       uint8_t  *sector_data;
+       TAILQ_ENTRY(wrsect) next;
+};
+
+/* write queue and track blocking skew */
+TAILQ_HEAD(wrsect_list, wrsect) write_queue;
+
+
+/* global variables describing disc and format requests */
+int     fd;                            /* device: file descriptor */
+char   *dev;                           /* device: name            */
+struct mmc_discinfo mmc_discinfo;      /* device: disc info       */
+
+char   *format_str;                    /* format: string representation */
+int     format_flags;                  /* format: attribute flags       */
+int     media_accesstype;              /* derived from current mmc cap  */
+int     check_surface;                 /* for rewritables               */
+int     imagefile_secsize;             /* for files                     */
+int     emul_packetsize;               /* for discs and files           */
+
+int     wrtrack_skew;
+int     meta_perc = UDF_META_PERC;
+float   meta_fract = (float) UDF_META_PERC / 100.0;
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * write queue implementation
+ */
+
+int
+udf_write_sector(void *sector, uint64_t location)
+{
+       struct wrsect *pos, *seekpos;
+
+
+       /* search location */
+       TAILQ_FOREACH_REVERSE(seekpos, &write_queue, wrsect_list, next) {
+               if (seekpos->sectornr <= location)
+                       break;
+       }
+       if ((seekpos == NULL) || (seekpos->sectornr != location)) {
+               pos = calloc(1, sizeof(struct wrsect));
+               if (pos == NULL)
+                       return ENOMEM;
+               /* allocate space for copy of sector data */
+               pos->sector_data = calloc(1, context.sector_size);
+               if (pos->sector_data == NULL)
+                       return ENOMEM;
+               pos->sectornr = location;
+
+               if (seekpos) {
+                       TAILQ_INSERT_AFTER(&write_queue, seekpos, pos, next);
+               } else {
+                       TAILQ_INSERT_HEAD(&write_queue, pos, next);
+               }       
+       } else {
+               pos = seekpos;
+       }
+       memcpy(pos->sector_data, sector, context.sector_size);
+
+       return 0;
+}
+
+
+/*
+ * Now all write requests are queued in the TAILQ, write them out to the
+ * disc/file image. Special care needs to be taken for devices that are only
+ * strict overwritable i.e. only in packet size chunks
+ *
+ * XXX support for growing vnd?
+ */
+
+int
+writeout_write_queue(void)
+{
+       struct wrsect *pos;
+       uint64_t offset;
+       uint64_t line_start, new_line_start;
+       uint32_t line_len, line_offset, relpos;
+       uint32_t blockingnr;
+       uint8_t *linebuf, *adr;
+
+       blockingnr  = layout.blockingnr;
+       line_len    = blockingnr   * context.sector_size;
+       line_offset = wrtrack_skew * context.sector_size;
+
+       linebuf     = malloc(line_len);
+       if (linebuf == NULL)
+               return ENOMEM;
+
+       pos = TAILQ_FIRST(&write_queue);
+       bzero(linebuf, line_len);
+
+       /*
+        * Always writing out in whole lines now; this is slightly wastefull
+        * on logical overwrite volumes but it reduces complexity and the loss
+        * is near zero compared to disc size.
+        */
+       line_start = (pos->sectornr - wrtrack_skew) / blockingnr;
+       TAILQ_FOREACH(pos, &write_queue, next) {
+               new_line_start = (pos->sectornr - wrtrack_skew) / blockingnr;
+               if (new_line_start != line_start) {
+                       /* write out */
+                       offset = (uint64_t) line_start * line_len + line_offset;
+#ifdef DEBUG
+                       printf("WRITEOUT %08"PRIu64" + %02d -- "
+                               "[%08"PRIu64"..%08"PRIu64"]\n",
+                               offset / context.sector_size, blockingnr,
+                               offset / context.sector_size,
+                               offset / context.sector_size + blockingnr-1);
+#endif
+                       if (pwrite(fd, linebuf, line_len, offset) < 0) {
+                               perror("Writing failed");
+                               return errno;
+                       }
+                       line_start = new_line_start;
+                       bzero(linebuf, line_len);
+               }
+
+               relpos = (pos->sectornr - wrtrack_skew) % blockingnr;
+               adr = linebuf + relpos * context.sector_size;
+               memcpy(adr, pos->sector_data, context.sector_size);
+       }
+       /* writeout last chunk */
+       offset = (uint64_t) line_start * line_len + line_offset;
+#ifdef DEBUG
+       printf("WRITEOUT %08"PRIu64" + %02d -- [%08"PRIu64"..%08"PRIu64"]\n",
+               offset / context.sector_size, blockingnr,
+               offset / context.sector_size,
+               offset / context.sector_size + blockingnr-1);
+#endif
+       if (pwrite(fd, linebuf, line_len, offset) < 0) {
+               perror("Writing failed");
+               return errno;
+       }
+
+       /* success */
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * mmc_discinfo and mmc_trackinfo readers modified from origional in udf main
+ * code in sys/fs/udf/
+ */
+
+#ifdef DEBUG
+static void
+udf_dump_discinfo(struct mmc_discinfo *di)
+{
+       char bits[128];
+
+       printf("Device/media info  :\n");
+       printf("\tMMC profile        0x%02x\n", di->mmc_profile);
+       printf("\tderived class      %d\n", di->mmc_class);
+       printf("\tsector size        %d\n", di->sector_size);
+       printf("\tdisc state         %d\n", di->disc_state);
+       printf("\tlast ses state     %d\n", di->last_session_state);
+       printf("\tbg format state    %d\n", di->bg_format_state);
+       printf("\tfrst track         %d\n", di->first_track);
+       printf("\tfst on last ses    %d\n", di->first_track_last_session);
+       printf("\tlst on last ses    %d\n", di->last_track_last_session);
+       printf("\tlink block penalty %d\n", di->link_block_penalty);
+       snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS, (uint64_t) di->disc_flags);
+       printf("\tdisc flags         %s\n", bits);
+       printf("\tdisc id            %x\n", di->disc_id);
+       printf("\tdisc barcode       %"PRIx64"\n", di->disc_barcode);
+
+       printf("\tnum sessions       %d\n", di->num_sessions);
+       printf("\tnum tracks         %d\n", di->num_tracks);
+
+       snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur);
+       printf("\tcapabilities cur   %s\n", bits);
+       snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap);
+       printf("\tcapabilities cap   %s\n", bits);
+       printf("\n");
+       printf("\tlast_possible_lba  %d\n", di->last_possible_lba);
+       printf("\n");
+}
+#else
+#define udf_dump_discinfo(a);
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_update_discinfo(struct mmc_discinfo *di)
+{
+       struct stat st;
+       struct disklabel  disklab;
+       struct partition *dp;
+       off_t size, sectors, secsize;
+       int partnr, error;
+
+       memset(di, 0, sizeof(struct mmc_discinfo));
+
+       /* check if we're on a MMC capable device, i.e. CD/DVD */
+       error = ioctl(fd, MMCGETDISCINFO, di);
+       if (error == 0)
+               return 0;
+
+       /* (re)fstat the file */
+       fstat(fd, &st);
+
+       if (S_ISREG(st.st_mode)) {
+               /* file support; we pick the minimum sector size allowed */
+               size = st.st_size;
+               secsize = imagefile_secsize;
+               sectors = size / secsize;
+       } else {
+               /*
+                * disc partition support; note we can't use DIOCGPART in
+                * userland so get disc label and use the stat info to get the
+                * partition number.
+                */
+               if (ioctl(fd, DIOCGDINFO, &disklab) == -1) {
+                       /* failed to get disclabel! */
+                       perror("disklabel");
+                       return errno;
+               }
+
+               /* get disk partition it refers to */
+               fstat(fd, &st);
+               partnr = DISKPART(st.st_rdev);
+               dp = &disklab.d_partitions[partnr];
+
+               /* TODO problem with last_possible_lba on resizable VND */
+               if (dp->p_size == 0) {
+                       perror("faulty disklabel partition returned, "
+                               "check label\n");
+                       return EIO;
+               }
+
+               sectors = dp->p_size;
+               secsize = disklab.d_secsize;
+       }
+
+       /* set up a disc info profile for partitions */
+       di->mmc_profile         = 0x01; /* disc type */
+       di->mmc_class           = MMC_CLASS_DISC;
+       di->disc_state          = MMC_STATE_CLOSED;
+       di->last_session_state  = MMC_STATE_CLOSED;
+       di->bg_format_state     = MMC_BGFSTATE_COMPLETED;
+       di->link_block_penalty  = 0;
+
+       di->mmc_cur     = MMC_CAP_RECORDABLE | MMC_CAP_REWRITABLE |
+               MMC_CAP_ZEROLINKBLK | MMC_CAP_HW_DEFECTFREE;
+       di->mmc_cap    = di->mmc_cur;
+       di->disc_flags = MMC_DFLAGS_UNRESTRICTED;
+
+       di->last_possible_lba = sectors - 1;
+       di->sector_size       = secsize;
+
+       di->num_sessions = 1;
+       di->num_tracks   = 1;
+
+       di->first_track  = 1;
+       di->first_track_last_session = di->last_track_last_session = 1;
+
+       return 0;
+}
+
+
+int
+udf_update_trackinfo(struct mmc_discinfo *di, struct mmc_trackinfo *ti)
+{
+       int error, class;
+
+       class = di->mmc_class;
+       if (class != MMC_CLASS_DISC) {
+               /* tracknr specified in struct ti */
+               error = ioctl(fd, MMCGETTRACKINFO, ti);
+               return error;
+       }
+
+       /* discs partition support */
+       if (ti->tracknr != 1)
+               return EIO;
+
+       /* create fake ti (TODO check for resized vnds) */
+       ti->sessionnr  = 1;
+
+       ti->track_mode = 0;     /* XXX */
+       ti->data_mode  = 0;     /* XXX */
+       ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID;
+
+       ti->track_start    = 0;
+       ti->packet_size    = emul_packetsize;
+
+       /* TODO support for resizable vnd */
+       ti->track_size    = di->last_possible_lba;
+       ti->next_writable = di->last_possible_lba;
+       ti->last_recorded = ti->next_writable;
+       ti->free_blocks   = 0;
+
+       return 0;
+}
+
+
+static int
+udf_setup_writeparams(struct mmc_discinfo *di)
+{
+       struct mmc_writeparams mmc_writeparams;
+       int error;
+
+       if (di->mmc_class == MMC_CLASS_DISC)
+               return 0;
+
+       /*
+        * only CD burning normally needs setting up, but other disc types
+        * might need other settings to be made. The MMC framework will set up
+        * the nessisary recording parameters according to the disc
+        * characteristics read in. Modifications can be made in the discinfo
+        * structure passed to change the nature of the disc.
+        */
+       memset(&mmc_writeparams, 0, sizeof(struct mmc_writeparams));
+       mmc_writeparams.mmc_class  = di->mmc_class;
+       mmc_writeparams.mmc_cur    = di->mmc_cur;
+
+       /*
+        * UDF dictates first track to determine track mode for the whole
+        * disc. [UDF 1.50/6.10.1.1, UDF 1.50/6.10.2.1]
+        * To prevent problems with a `reserved' track in front we start with
+        * the 2nd track and if that is not valid, go for the 1st.
+        */
+       mmc_writeparams.tracknr = 2;
+       mmc_writeparams.data_mode  = MMC_DATAMODE_DEFAULT;      /* XA disc */
+       mmc_writeparams.track_mode = MMC_TRACKMODE_DEFAULT;     /* data */
+
+       error = ioctl(fd, MMCSETUPWRITEPARAMS, &mmc_writeparams);
+       if (error) {
+               mmc_writeparams.tracknr = 1;
+               error = ioctl(fd, MMCSETUPWRITEPARAMS, &mmc_writeparams);
+       }
+       return error;
+}
+
+
+static void
+udf_synchronise_caches(void)
+{
+       struct mmc_op mmc_op;
+
+       bzero(&mmc_op, sizeof(struct mmc_op));
+       mmc_op.operation = MMC_OP_SYNCHRONISECACHE;
+
+       /* this device might not know this ioct, so just be ignorant */
+       (void) ioctl(fd, MMCOP, &mmc_op);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_prepare_disc(void)
+{
+       struct mmc_trackinfo ti;
+       struct mmc_op        op;
+       int tracknr, error;
+
+       /* If the last track is damaged, repair it */
+       ti.tracknr = mmc_discinfo.last_track_last_session;
+       error = udf_update_trackinfo(&mmc_discinfo, &ti);
+       if (error)
+               return error;
+
+       if (ti.flags & MMC_TRACKINFO_DAMAGED) {
+               /*
+                * Need to repair last track before anything can be done.
+                * this is an optional command, so ignore its error but report
+                * warning.
+                */
+               memset(&op, 0, sizeof(op));
+               op.operation   = MMC_OP_REPAIRTRACK;
+               op.mmc_profile = mmc_discinfo.mmc_profile;
+               op.tracknr     = ti.tracknr;
+               error = ioctl(fd, MMCOP, &op);
+
+               if (error)
+                       (void)printf("Drive can't explicitly repair last "
+                               "damaged track, but it might autorepair\n");
+       }
+       /* last track (if any) might not be damaged now, operations are ok now */
+
+       /* setup write parameters from discinfo */
+       error = udf_setup_writeparams(&mmc_discinfo);
+       if (error)
+               return error;
+
+       /* if the drive is not sequential, we're done */
+       if ((mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) == 0)
+               return 0;
+
+#ifdef notyet
+       /* if last track is not the reserved but an empty track, unreserve it */
+       if (ti.flags & MMC_TRACKINFO_BLANK) {
+               if (ti.flags & MMC_TRACKINFO_RESERVED == 0) {
+                       memset(&op, 0, sizeof(op));
+                       op.operation   = MMC_OP_UNRESERVETRACK;
+                       op.mmc_profile = mmc_discinfo.mmc_profile;
+                       op.tracknr     = ti.tracknr;
+                       error = ioctl(fd, MMCOP, &op);
+                       if (error)
+                               return error;
+
+                       /* update discinfo since it changed by the operation */
+                       error = udf_update_discinfo(&mmc_discinfo);
+                       if (error)
+                               return error;
+               }
+       }
+#endif
+
+       /* close the last session if its still open */
+       if (mmc_discinfo.last_session_state == MMC_STATE_INCOMPLETE) {
+               printf("Closing last open session if present\n");
+               /* close all associated tracks */
+               tracknr = mmc_discinfo.first_track_last_session;
+               while (tracknr <= mmc_discinfo.last_track_last_session) {
+                       ti.tracknr = tracknr;
+                       error = udf_update_trackinfo(&mmc_discinfo, &ti);
+                       if (error)
+                               return error;
+                       printf("\tClosing open track %d\n", tracknr);
+                       memset(&op, 0, sizeof(op));
+                       op.operation   = MMC_OP_CLOSETRACK;
+                       op.mmc_profile = mmc_discinfo.mmc_profile;
+                       op.tracknr     = tracknr;
+                       error = ioctl(fd, MMCOP, &op);
+                       if (error)
+                               return error;
+                       tracknr ++;
+               }
+               printf("Closing session\n");
+               memset(&op, 0, sizeof(op));
+               op.operation   = MMC_OP_CLOSESESSION;
+               op.mmc_profile = mmc_discinfo.mmc_profile;
+               op.sessionnr   = mmc_discinfo.num_sessions;
+               error = ioctl(fd, MMCOP, &op);
+               if (error)
+                       return error;
+
+               /* update discinfo since it changed by the operations */
+               error = udf_update_discinfo(&mmc_discinfo);
+               if (error)
+                       return error;
+       }
+
+       if (format_flags & FORMAT_TRACK512) {
+               /* get last track again */
+               ti.tracknr = mmc_discinfo.last_track_last_session;
+               error = udf_update_trackinfo(&mmc_discinfo, &ti);
+               if (error)
+                       return error;
+
+               /* Split up the space at 512 for iso cd9660 hooking */
+               memset(&op, 0, sizeof(op));
+               op.operation   = MMC_OP_RESERVETRACK_NWA;       /* UPTO nwa */
+               op.mmc_profile = mmc_discinfo.mmc_profile;
+               op.extent      = 512;                           /* size */
+               error = ioctl(fd, MMCOP, &op);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_surface_check(void)
+{
+       uint32_t loc, block_bytes;
+       uint32_t sector_size, blockingnr, bpos;
+       uint8_t *buffer;
+       int error, num_errors;
+
+       sector_size = context.sector_size;
+       blockingnr  = layout.blockingnr;
+
+       block_bytes = layout.blockingnr * sector_size;
+       if ((buffer = malloc(block_bytes)) == NULL)
+               return ENOMEM;
+
+       /* set all one to not kill Flash memory? */
+       for (bpos = 0; bpos < block_bytes; bpos++)
+               buffer[bpos] = 0x00;
+
+       printf("\nChecking disc surface : phase 1 - writing\n");
+       num_errors = 0;
+       loc = layout.first_lba;
+       while (loc <= layout.last_lba) {
+               /* write blockingnr sectors */
+               error = pwrite(fd, buffer, block_bytes, loc*sector_size);
+               printf("   %08d + %d (%02d %%)\r", loc, blockingnr,
+                       (int)((100.0 * loc)/layout.last_lba));
+               fflush(stdout);
+               if (error == -1) {
+                       /* block is bad */
+                       printf("BAD block at %08d + %d         \n",
+                               loc, layout.blockingnr);
+                       if ((error = udf_register_bad_block(loc))) {
+                               free(buffer);
+                               return error;
+                       }
+                       num_errors ++;
+               }
+               loc += layout.blockingnr;
+       }
+
+       printf("\nChecking disc surface : phase 2 - reading\n");
+       num_errors = 0;
+       loc = layout.first_lba;
+       while (loc <= layout.last_lba) {
+               /* read blockingnr sectors */
+               error = pread(fd, buffer, block_bytes, loc*sector_size);
+               printf("   %08d + %d (%02d %%)\r", loc, blockingnr,
+                       (int)((100.0 * loc)/layout.last_lba));
+               fflush(stdout);
+               if (error == -1) {
+                       /* block is bad */
+                       printf("BAD block at %08d + %d         \n",
+                               loc, layout.blockingnr);
+                       if ((error = udf_register_bad_block(loc))) {
+                               free(buffer);
+                               return error;
+                       }
+                       num_errors ++;
+               }
+               loc += layout.blockingnr;
+       }
+       printf("Scan complete : %d bad blocks found\n", num_errors);
+       free(buffer);
+
+       return 0;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_do_newfs(void)
+{
+       int error;
+
+       error = udf_do_newfs_prefix();
+       if (error)
+               return error;
+       error = udf_do_rootdir();
+       if (error)
+               return error;
+       error = udf_do_newfs_postfix();
+
+       return error;
+}
+
+
+
+/* --------------------------------------------------------------------- */
+
+static void
+usage(void)
+{
+       (void)fprintf(stderr, "Usage: %s [-cFM] [-L loglabel] "
+           "[-P discid] [-S sectorsize] [-s size] [-p perc] "
+           "[-t gmtoff] [-v min_udf] [-V max_udf] special\n", getprogname());
+       exit(EXIT_FAILURE);
+}
+
+
+int
+main(int argc, char **argv)
+{
+       struct tm *tm;
+       struct stat st;
+       time_t now;
+       off_t setsize;
+       char  scrap[255], *colon;
+       int ch, req_enable, req_disable, force;
+       int error;
+
+       setprogname(argv[0]);
+
+       /* initialise */
+       format_str    = strdup("");
+       req_enable    = req_disable = 0;
+       format_flags  = FORMAT_INVALID;
+       force         = 0;
+       check_surface = 0;
+       setsize       = 0;
+       imagefile_secsize = 512;        /* minimum allowed sector size */
+       emul_packetsize   = 32;         /* reasonable default */
+
+       srandom((unsigned long) time(NULL));
+       udf_init_create_context();
+       context.app_name         = "*NetBSD newfs";
+       context.app_version_main = APP_VERSION_MAIN;
+       context.app_version_sub  = APP_VERSION_SUB;
+       context.impl_name        = IMPL_NAME;
+
+       /* minimum and maximum UDF versions we advise */
+       context.min_udf = 0x201;
+       context.max_udf = 0x201;
+
+       /* use user's time zone as default */
+       (void)time(&now);
+       tm = localtime(&now);
+       context.gmtoff = tm->tm_gmtoff;
+
+       /* process options */
+       while ((ch = getopt(argc, argv, "cFL:Mp:P:s:S:B:t:v:V:")) != -1) {
+               switch (ch) {
+               case 'c' :
+                       check_surface = 1;
+                       break;
+               case 'F' :
+                       force = 1;
+                       break;
+               case 'L' :
+                       if (context.logvol_name) free(context.logvol_name);
+                       context.logvol_name = strdup(optarg);
+                       break;
+               case 'M' :
+                       req_disable |= FORMAT_META;
+                       break;
+               case 'p' :
+                       meta_perc = a_num(optarg, "meta_perc");
+                       /* limit to `sensible` values */
+                       meta_perc = MIN(meta_perc, 99);
+                       meta_perc = MAX(meta_perc, 1);
+                       meta_fract = (float) meta_perc/100.0;
+                       break;
+               case 'v' :
+                       context.min_udf = a_udf_version(optarg, "min_udf");
+                       if (context.min_udf > context.max_udf)
+                               context.max_udf = context.min_udf;
+                       break;
+               case 'V' :
+                       context.max_udf = a_udf_version(optarg, "max_udf");
+                       if (context.min_udf > context.max_udf)
+                               context.min_udf = context.max_udf;
+                       break;
+               case 'P' :
+                       /* check if there is a ':' in the name */
+                       if ((colon = strstr(optarg, ":"))) {
+                               if (context.volset_name)
+                                       free(context.volset_name);
+                               *colon = 0;
+                               context.volset_name = strdup(optarg);
+                               optarg = colon+1;
+                       }
+                       if (context.primary_name)
+                               free(context.primary_name);
+                       if ((strstr(optarg, ":"))) {
+                               perror("primary name can't have ':' in its name");
+                               return EXIT_FAILURE;
+                       }
+                       context.primary_name = strdup(optarg);
+                       break;
+               case 's' :
+                       /* support for files, set file size */
+                       /* XXX support for formatting recordables on vnd/file? */
+                       if (dehumanize_number(optarg, &setsize) < 0) {
+                               perror("can't parse size argument");
+                               return EXIT_FAILURE;
+                       }
+                       setsize = MAX(0, setsize);
+                       break;
+               case 'S' :
+                       imagefile_secsize = a_num(optarg, "secsize");
+                       imagefile_secsize = MAX(512, imagefile_secsize);
+                       break;
+               case 'B' :
+                       emul_packetsize = a_num(optarg,
+                               "blockingnr, packetsize");
+                       emul_packetsize = MAX(emul_packetsize, 1);
+                       emul_packetsize = MIN(emul_packetsize, 32);
+                       break;
+               case 't' :
+                       /* time zone overide */
+                       context.gmtoff = a_num(optarg, "gmtoff");
+                       break;
+               default  :
+                       usage();
+                       /* NOTREACHED */
+               }
+       }
+
+       if (optind + 1 != argc)
+               usage();
+
+       /* get device and directory specifier */
+       dev = argv[optind];
+
+       /* open device */
+       if ((fd = open(dev, O_RDWR, 0)) == -1) {
+               /* check if we need to create a file */
+               fd = open(dev, O_RDONLY, 0);
+               if (fd > 0) {
+                       perror("device is there but can't be opened for read/write");
+                       return EXIT_FAILURE;
+               }
+               if (!force) {
+                       perror("can't open device");
+                       return EXIT_FAILURE;
+               }
+               if (setsize == 0) {
+                       perror("need to create image file but no size specified");
+                       return EXIT_FAILURE;
+               }
+               /* need to create a file */
+               fd = open(dev, O_RDWR | O_CREAT | O_TRUNC, 0777);
+               if (fd == -1) {
+                       perror("can't create image file");
+                       return EXIT_FAILURE;
+               }
+       }
+
+       /* stat the device */
+       if (fstat(fd, &st) != 0) {
+               perror("can't stat the device");
+               close(fd);
+               return EXIT_FAILURE;
+       }
+
+       if (S_ISREG(st.st_mode)) {
+               if (setsize == 0)
+                       setsize = st.st_size;
+               /* sanitise arguments */
+               imagefile_secsize &= ~511;
+               setsize &= ~(imagefile_secsize-1);
+
+               if (ftruncate(fd, setsize)) {
+                       perror("can't resize file");
+                       return EXIT_FAILURE;
+               }
+       }
+
+       /* formatting can only be done on raw devices */
+       if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) {
+               printf("%s is not a raw device\n", dev);
+               close(fd);
+               return EXIT_FAILURE;
+       }
+
+       /* just in case something went wrong, synchronise the drive's cache */
+       udf_synchronise_caches();
+
+       /* get 'disc' information */
+       error = udf_update_discinfo(&mmc_discinfo);
+       if (error) {
+               perror("can't retrieve discinfo");
+               close(fd);
+               return EXIT_FAILURE;
+       }
+
+       /* derive disc identifiers when not specified and check given */
+       error = udf_proces_names();
+       if (error) {
+               /* error message has been printed */
+               close(fd);
+               return EXIT_FAILURE;
+       }
+
+       /* derive newfs disc format from disc profile */
+       error = udf_derive_format(req_enable, req_disable, force);
+       if (error)  {
+               /* error message has been printed */
+               close(fd);
+               return EXIT_FAILURE;
+       }
+
+       udf_dump_discinfo(&mmc_discinfo);
+       printf("Formatting disc compatible with UDF version %x to %x\n\n",
+               context.min_udf, context.max_udf);
+       (void)snprintb(scrap, sizeof(scrap), FORMAT_FLAGBITS,
+           (uint64_t) format_flags);
+       printf("UDF properties       %s\n", scrap);
+       printf("Volume set          `%s'\n", context.volset_name);
+       printf("Primary volume      `%s`\n", context.primary_name);
+       printf("Logical volume      `%s`\n", context.logvol_name);
+       if (format_flags & FORMAT_META)
+               printf("Metadata percentage  %d %%\n", meta_perc);
+       printf("\n");
+
+       /* prepare disc if nessisary (recordables mainly) */
+       error = udf_prepare_disc();
+       if (error) {
+               perror("preparing disc failed");
+               close(fd);
+               return EXIT_FAILURE;
+       };
+
+       /* setup sector writeout queue's */
+       TAILQ_INIT(&write_queue);
+
+       /* perform the newfs itself */
+       error = udf_do_newfs();
+       if (!error) {
+               /* write out sectors */
+               error = writeout_write_queue();
+       }
+
+       /* in any case, synchronise the drive's cache to prevent lockups */
+       udf_synchronise_caches();
+
+       close(fd);
+       if (error)
+               return EXIT_FAILURE;
+
+       return EXIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+
diff --git a/sbin/newfs_udf/newfs_udf.h b/sbin/newfs_udf/newfs_udf.h
new file mode 100644 (file)
index 0000000..1c21a0b
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#ifndef _FS_UDF_NEWFS_UDF_H_
+#define _FS_UDF_NEWFS_UDF_H_
+
+/* general settings */
+#define UDF_512_TRACK  0       /* NOT recommended */
+#define UDF_META_PERC  20      /* picked */
+
+/* Identifying myself */
+#define APP_VERSION_MAIN       0
+#define APP_VERSION_SUB                5
+#define IMPL_NAME              "*NetBSD userland UDF"
+
+
+/* global variables describing disc and format requests */
+extern int      fd;                    /* device: file descriptor */
+extern char    *dev;                   /* device: name            */
+extern struct mmc_discinfo mmc_discinfo;/* device: disc info      */
+
+extern char    *format_str;            /* format: string representation */
+extern int      format_flags;          /* format: attribute flags       */
+extern int      media_accesstype;      /* derived from current mmc cap  */
+extern int      check_surface;         /* for rewritables               */
+
+extern int      wrtrack_skew;
+extern int      meta_perc;
+extern float    meta_fract;
+
+
+/* shared structure between udf_create.c users */
+struct udf_create_context context;
+struct udf_disclayout     layout;
+
+/* prototypes */
+int udf_write_sector(void *sector, uint64_t location);
+int udf_update_trackinfo(struct mmc_discinfo *di, struct mmc_trackinfo *ti);
+
+/* tmp */
+int writeout_write_queue(void);
+int udf_surface_check(void);
+
+#endif /* _FS_UDF_UDF_WRITE_H_ */
diff --git a/sbin/newfs_udf/udf_create.c b/sbin/newfs_udf/udf_create.c
new file mode 100644 (file)
index 0000000..e40c708
--- /dev/null
@@ -0,0 +1,2392 @@
+/* $NetBSD: udf_create.c,v 1.24 2013/10/19 01:09:59 christos Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: udf_create.c,v 1.24 2013/10/19 01:09:59 christos Exp $");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include "unicode.h"
+#include "udf_create.h"
+
+
+#if 0
+# ifndef DEBUG
+#   define DEBUG
+#  endif
+#endif
+
+/*
+ * NOTE that there is some overlap between this code and the udf kernel fs.
+ * This is intentially though it might better be factored out one day.
+ */
+
+void
+udf_init_create_context(void)
+{
+       /* clear */
+       memset(&context, 0, sizeof(struct udf_create_context));
+
+       /* fill with defaults currently known */
+       context.dscrver = 3;
+       context.min_udf = 0x0102;
+       context.max_udf = 0x0260;
+       context.serialnum = 1;          /* default */
+
+       context.gmtoff  = 0;
+       context.sector_size = 512;      /* minimum for UDF */
+
+       context.logvol_name  = NULL;
+       context.primary_name = NULL;
+       context.volset_name  = NULL;
+       context.fileset_name = NULL;
+
+       /* most basic identification */
+       context.app_name         = "*NetBSD";
+       context.app_version_main = 0;
+       context.app_version_sub  = 0;
+       context.impl_name        = "*NetBSD";
+
+       context.vds_seq = 0;            /* first one starts with zero */
+
+       /* Minimum value of 16 : UDF 3.2.1.1, 3.3.3.4. */
+       context.unique_id       = 0x10;
+
+       context.num_files       = 0;
+       context.num_directories = 0;
+
+       context.data_part          = 0;
+       context.metadata_part      = 0;
+       context.metadata_alloc_pos = 0;
+       context.data_alloc_pos     = 0;
+}
+
+
+/* version can be specified as 0xabc or a.bc */
+static int
+parse_udfversion(const char *pos, uint32_t *version) {
+       int hex = 0;
+       char c1, c2, c3, c4;
+
+       *version = 0;
+       if (*pos == '0') {
+               pos++;
+               /* expect hex format */
+               hex = 1;
+               if (*pos++ != 'x')
+                       return 1;
+       }
+
+       c1 = *pos++;
+       if (c1 < '0' || c1 > '9')
+               return 1;
+       c1 -= '0';
+
+       c2 = *pos++;
+       if (!hex) {
+               if (c2 != '.')
+                       return 1;
+               c2 = *pos++;
+       }
+       if (c2 < '0' || c2 > '9')
+               return 1;
+       c2 -= '0';
+
+       c3 = *pos++;
+       if (c3 < '0' || c3 > '9')
+               return 1;
+       c3 -= '0';
+
+       c4 = *pos++;
+       if (c4 != 0)
+               return 1;
+
+       *version = c1 * 0x100 + c2 * 0x10 + c3;
+       return 0;
+}
+
+
+/* parse a given string for an udf version */
+int
+a_udf_version(const char *s, const char *id_type)
+{
+       uint32_t version;
+
+       if (parse_udfversion(s, &version))
+               errx(1, "unknown %s id %s; specify as hex or float", id_type, s);
+       return version;
+}
+
+
+static uint32_t
+udf_space_bitmap_len(uint32_t part_size)
+{
+       return  sizeof(struct space_bitmap_desc)-1 +
+               part_size/8;
+}
+
+
+static uint32_t
+udf_bytes_to_sectors(uint64_t bytes)
+{
+       uint32_t sector_size = layout.sector_size;
+       return (bytes + sector_size -1) / sector_size;
+}
+
+
+int
+udf_calculate_disc_layout(int format_flags, int min_udf,
+       uint32_t wrtrack_skew,
+       uint32_t first_lba, uint32_t last_lba,
+       uint32_t sector_size, uint32_t blockingnr,
+       uint32_t sparable_blocks, float meta_fract)
+{
+       uint64_t kbsize, bytes;
+       uint32_t sparable_blockingnr;
+       uint32_t align_blockingnr;
+       uint32_t pos, mpos;
+
+       /* clear */
+       memset(&layout, 0, sizeof(layout));
+
+       /* fill with parameters */
+       layout.wrtrack_skew    = wrtrack_skew;
+       layout.first_lba       = first_lba;
+       layout.last_lba        = last_lba;
+       layout.sector_size     = sector_size;
+       layout.blockingnr      = blockingnr;
+       layout.sparable_blocks = sparable_blocks;
+
+       /* start disc layouting */
+
+       /*
+        * location of iso9660 vrs is defined as first sector AFTER 32kb,
+        * minimum `sector size' 2048
+        */
+       layout.iso9660_vrs = ((32*1024 + sector_size - 1) / sector_size)
+               + first_lba;
+
+       /* anchor starts at specified offset in sectors */
+       layout.anchors[0] = first_lba + 256;
+       if (format_flags & FORMAT_TRACK512)
+               layout.anchors[0] = first_lba + 512;
+       layout.anchors[1] = last_lba - 256;
+       layout.anchors[2] = last_lba;
+
+       /* update workable space */
+       first_lba = layout.anchors[0] + blockingnr;
+       last_lba  = layout.anchors[1] - 1;
+
+       /* XXX rest of anchor packet can be added to unallocated space descr */
+
+       /* reserve space for VRS and VRS copy and associated tables */
+       layout.vds_size = MAX(16, blockingnr);     /* UDF 2.2.3.1+2 */
+       layout.vds1 = first_lba;
+       first_lba += layout.vds_size;              /* next packet */
+
+       if (format_flags & FORMAT_SEQUENTIAL) {
+               /* for sequential, append them ASAP */
+               layout.vds2 = first_lba;
+               first_lba += layout.vds_size;
+       } else {
+               layout.vds2 = layout.anchors[1] - layout.vds_size;
+               last_lba = layout.vds2 - 1;     /* XXX -1 ?? */
+       }
+
+       /* reserve space for logvol integrity sequence */
+       layout.lvis_size = MAX(8192/sector_size, 2 * blockingnr);
+       if (format_flags & FORMAT_VAT)
+               layout.lvis_size = 2;
+       if (format_flags & FORMAT_WORM)
+               layout.lvis_size = 64 * blockingnr;
+
+       /* TODO skip bad blocks in LVID sequence; for now use f.e. */
+//first_lba+=128;
+       layout.lvis = first_lba;
+       first_lba += layout.lvis_size;
+
+       /* initial guess of UDF partition size */
+       layout.part_start_lba = first_lba;
+       layout.part_size_lba = last_lba - layout.part_start_lba;
+
+       /* all non sequential media needs an unallocated space bitmap */
+       layout.alloc_bitmap_dscr_size = 0;
+       if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) {
+               bytes = udf_space_bitmap_len(layout.part_size_lba);
+               layout.alloc_bitmap_dscr_size = udf_bytes_to_sectors(bytes);
+
+               /* XXX freed space map when applicable */
+       }
+
+       /*
+        * Note that for (bug) compatibility with version UDF 2.00 (fixed in
+        * 2.01 and higher) the blocking size needs to be 32 sectors otherwise
+        * the drive's blockingnr.
+        */
+
+       sparable_blockingnr = blockingnr;
+       if (min_udf <= 0x200)
+               sparable_blockingnr = 32;
+
+       align_blockingnr = blockingnr;
+       if (format_flags & (FORMAT_SPARABLE | FORMAT_META))
+               align_blockingnr = sparable_blockingnr;
+
+       layout.align_blockingnr    = align_blockingnr;
+       layout.sparable_blockingnr = sparable_blockingnr;
+
+       /*
+        * Align partition LBA space to blocking granularity. Not strickly
+        * nessisary for non sparables but safer for the VRS data since it is
+        * not updated sporadically
+        */
+
+       if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) {
+#ifdef DEBUG
+               printf("Lost %d slack sectors at start\n", UDF_ROUNDUP(
+                       first_lba - wrtrack_skew, align_blockingnr) -
+                               (first_lba - wrtrack_skew));
+               printf("Lost %d slack sectors at end\n",
+                       (first_lba - wrtrack_skew) - UDF_ROUNDDOWN(
+                               first_lba - wrtrack_skew, align_blockingnr));
+#endif
+
+               first_lba = UDF_ROUNDUP( first_lba - wrtrack_skew,
+                               align_blockingnr);
+               last_lba  = UDF_ROUNDDOWN(last_lba - wrtrack_skew,
+                               align_blockingnr);
+       }
+
+       if ((format_flags & FORMAT_SPARABLE) == 0)
+               layout.sparable_blocks = 0;
+
+       if (format_flags & FORMAT_SPARABLE) {
+               layout.sparable_area_size =
+                       layout.sparable_blocks * sparable_blockingnr;
+
+               /* a sparing table descriptor is a whole blockingnr sectors */
+               layout.sparing_table_dscr_lbas = sparable_blockingnr;
+
+               /* place the descriptors at the start and end of the area */
+               layout.spt_1 = first_lba;
+               first_lba += layout.sparing_table_dscr_lbas;
+
+               layout.spt_2 = last_lba - layout.sparing_table_dscr_lbas;
+               last_lba -= layout.sparing_table_dscr_lbas;
+
+               /* allocate sparable section */
+               layout.sparable_area = first_lba;
+               first_lba += layout.sparable_area_size;
+       }
+
+       /* update guess of UDF partition size */
+       layout.part_start_lba = first_lba;
+       layout.part_size_lba = last_lba - layout.part_start_lba;
+
+       /* determine partition selection for data and metadata */
+       context.data_part     = 0;
+       context.metadata_part = context.data_part;
+       if ((format_flags & FORMAT_VAT) || (format_flags & FORMAT_META))
+               context.metadata_part = context.data_part + 1;
+
+       /*
+        * Pick fixed logical space sector numbers for main FSD, rootdir and
+        * unallocated space. The reason for this pre-allocation is that they
+        * are referenced in the volume descriptor sequence and hence can't be
+        * allocated later.
+        */
+       pos = 0;
+       layout.unalloc_space = pos;
+       pos += layout.alloc_bitmap_dscr_size;
+
+       /* claim metadata descriptors and partition space [UDF 2.2.10] */
+       if (format_flags & FORMAT_META) {
+               /* note: all in backing partition space */
+               layout.meta_file   = pos++;
+               layout.meta_bitmap = pos++;;
+               layout.meta_mirror = layout.part_size_lba-1;
+               layout.meta_alignment  = MAX(blockingnr, sparable_blockingnr);
+               layout.meta_blockingnr = MAX(layout.meta_alignment, 32);
+
+               /* calculate our partition length and store in sectors */
+               layout.meta_part_size_lba = layout.part_size_lba * meta_fract;
+               layout.meta_part_size_lba = MAX(layout.meta_part_size_lba, 32);
+               layout.meta_part_size_lba =
+                       UDF_ROUNDDOWN(layout.meta_part_size_lba, layout.meta_blockingnr);
+
+               /* calculate positions */
+               bytes = udf_space_bitmap_len(layout.meta_part_size_lba);
+               layout.meta_bitmap_dscr_size = udf_bytes_to_sectors(bytes);
+
+               layout.meta_bitmap_space = pos;
+               pos += layout.meta_bitmap_dscr_size;
+
+               layout.meta_part_start_lba  = UDF_ROUNDUP(pos, layout.meta_alignment);
+       }
+
+       mpos = (context.metadata_part == context.data_part) ? pos : 0;
+       layout.fsd           = mpos;    mpos += 1;
+       layout.rootdir       = mpos;    mpos += 1;
+       layout.vat           = mpos;    mpos += 1;      /* if present */
+
+#if 0
+       printf("Summary so far\n");
+       printf("\tiso9660_vrs\t\t%d\n", layout.iso9660_vrs);
+       printf("\tanchor0\t\t\t%d\n", layout.anchors[0]);
+       printf("\tanchor1\t\t\t%d\n", layout.anchors[1]);
+       printf("\tanchor2\t\t\t%d\n", layout.anchors[2]);
+       printf("\tvds_size\t\t%d\n", layout.vds_size);
+       printf("\tvds1\t\t\t%d\n", layout.vds1);
+       printf("\tvds2\t\t\t%d\n", layout.vds2);
+       printf("\tlvis_size\t\t%d\n", layout.lvis_size);
+       printf("\tlvis\t\t\t%d\n", layout.lvis);
+       if (format_flags & FORMAT_SPARABLE) {
+               printf("\tsparable size\t\t%d\n", layout.sparable_area_size);
+               printf("\tsparable\t\t%d\n", layout.sparable_area);
+       }
+       printf("\tpartition start lba\t%d\n", layout.part_start_lba);
+       printf("\tpartition size\t\t%d KiB, %d MiB\n",
+               (layout.part_size_lba * sector_size) / 1024,
+               (layout.part_size_lba * sector_size) / (1024*1024));
+       if ((format_flags & FORMAT_SEQUENTIAL) == 0) {
+               printf("\tpart bitmap start\t%d\n",   layout.unalloc_space);
+               printf("\t\tfor %d lba\n", layout.alloc_bitmap_dscr_size);
+       }
+       if (format_flags & FORMAT_META) {
+               printf("\tmeta blockingnr\t\t%d\n", layout.meta_blockingnr);
+               printf("\tmeta alignment\t\t%d\n",  layout.meta_alignment);
+               printf("\tmeta size\t\t%d KiB, %d MiB\n",
+                       (layout.meta_part_size_lba * sector_size) / 1024,
+                       (layout.meta_part_size_lba * sector_size) / (1024*1024));
+               printf("\tmeta file\t\t%d\n", layout.meta_file);
+               printf("\tmeta mirror\t\t%d\n", layout.meta_mirror);
+               printf("\tmeta bitmap\t\t%d\n", layout.meta_bitmap);
+               printf("\tmeta bitmap start\t%d\n", layout.meta_bitmap_space);
+               printf("\t\tfor %d lba\n", layout.meta_bitmap_dscr_size);
+               printf("\tmeta space start\t%d\n",  layout.meta_part_start_lba);
+               printf("\t\tfor %d lba\n", layout.meta_part_size_lba);
+       }
+       printf("\n");
+#endif
+
+       kbsize = (uint64_t) last_lba * sector_size;
+       printf("Total space on this medium approx. "
+                       "%"PRIu64" KiB, %"PRIu64" MiB\n",
+                       kbsize/1024, kbsize/(1024*1024));
+       kbsize = (uint64_t)(layout.part_size_lba - layout.alloc_bitmap_dscr_size
+               - layout.meta_bitmap_dscr_size) * sector_size;
+       printf("Free space on this volume approx.  "
+                       "%"PRIu64" KiB, %"PRIu64" MiB\n\n",
+                       kbsize/1024, kbsize/(1024*1024));
+
+       return 0;
+}
+
+
+int
+udf_validate_tag_sum(union dscrptr *dscr)
+{
+       struct desc_tag *tag = &dscr->tag;
+       uint8_t *pos, sum, cnt;
+
+       /* calculate TAG header checksum */
+       pos = (uint8_t *) tag;
+       sum = 0;
+
+       for(cnt = 0; cnt < 16; cnt++) {
+               if (cnt != 4) sum += *pos;
+               pos++;
+       };
+       tag->cksum = sum;       /* 8 bit */
+
+       return 0;
+}
+
+
+/* assumes sector number of descriptor to be allready present */
+int
+udf_validate_tag_and_crc_sums(union dscrptr *dscr)
+{
+       struct desc_tag *tag = &dscr->tag;
+       uint16_t crc;
+
+       /* check payload CRC if applicable */
+       if (udf_rw16(tag->desc_crc_len) > 0) {
+               crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH,
+                       udf_rw16(tag->desc_crc_len));
+               tag->desc_crc = udf_rw16(crc);
+       };
+
+       /* calculate TAG header checksum */
+       return udf_validate_tag_sum(dscr);
+}
+
+
+void
+udf_inittag(struct desc_tag *tag, int tagid, uint32_t loc)
+{
+       tag->id                 = udf_rw16(tagid);
+       tag->descriptor_ver     = udf_rw16(context.dscrver);
+       tag->cksum              = 0;
+       tag->reserved           = 0;
+       tag->serial_num         = udf_rw16(context.serialnum);
+       tag->tag_loc            = udf_rw32(loc);
+}
+
+
+int
+udf_create_anchor(int num)
+{
+       struct anchor_vdp *avdp;
+       uint32_t vds_extent_len = layout.vds_size * context.sector_size;
+       
+       if ((avdp = calloc(1, context.sector_size)) == NULL)
+               return ENOMEM;
+
+       udf_inittag(&avdp->tag, TAGID_ANCHOR, layout.anchors[num]);
+
+       avdp->main_vds_ex.loc = udf_rw32(layout.vds1);
+       avdp->main_vds_ex.len = udf_rw32(vds_extent_len);
+
+       avdp->reserve_vds_ex.loc = udf_rw32(layout.vds2);
+       avdp->reserve_vds_ex.len = udf_rw32(vds_extent_len);
+
+       /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */
+       avdp->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH);
+
+       context.anchors[num] = avdp;
+       return 0;
+}
+
+
+void
+udf_create_terminator(union dscrptr *dscr, uint32_t loc)
+{
+       memset(dscr, 0, context.sector_size);
+       udf_inittag(&dscr->tag, TAGID_TERM, loc);
+
+       /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */
+       dscr->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH);
+}
+
+
+void
+udf_osta_charset(struct charspec *charspec)
+{
+       memset(charspec, 0, sizeof(*charspec));
+       charspec->type = 0;
+       strcpy((char *) charspec->inf, "OSTA Compressed Unicode");
+}
+
+
+void
+udf_encode_osta_id(char *osta_id, uint16_t len, char *text)
+{
+       uint16_t  u16_name[1024];
+       uint8_t  *pos;
+       uint16_t *pos16;
+
+       memset(osta_id, 0, len);
+       if (!text || (strlen(text) == 0)) return;
+
+       memset(u16_name, 0, sizeof(uint16_t) * 1023);
+
+       /* convert ascii to 16 bits unicode */
+       pos   = (uint8_t *) text;
+       pos16 = u16_name;
+       while (*pos) {
+               *pos16 = *pos;
+               pos++; pos16++;
+       };
+       *pos16 = 0;
+
+       udf_CompressUnicode(len, 8, (unicode_t *) u16_name, (byte *) osta_id);
+
+       /* Ecma 167/7.2.13 states that length is recorded in the last byte */
+       osta_id[len-1] = strlen(text)+1;
+}
+
+
+/* first call udf_set_regid and then the suffix */
+void
+udf_set_regid(struct regid *regid, char const *name)
+{
+       memset(regid, 0, sizeof(*regid));
+       regid->flags    = 0;            /* not dirty and not protected */
+       strcpy((char *) regid->id, name);
+}
+
+
+void
+udf_add_domain_regid(struct regid *regid)
+{
+       uint16_t *ver;
+
+       ver  = (uint16_t *) regid->id_suffix;
+       *ver = udf_rw16(context.min_udf);
+}
+
+
+void
+udf_add_udf_regid(struct regid *regid)
+{
+       uint16_t *ver;
+
+       ver  = (uint16_t *) regid->id_suffix;
+       *ver = udf_rw16(context.min_udf);
+
+       regid->id_suffix[2] = 4;        /* unix */
+       regid->id_suffix[3] = 8;        /* NetBSD */
+}
+
+
+void
+udf_add_impl_regid(struct regid *regid)
+{
+       regid->id_suffix[0] = 4;        /* unix */
+       regid->id_suffix[1] = 8;        /* NetBSD */
+}
+
+
+void
+udf_add_app_regid(struct regid *regid)
+{
+       regid->id_suffix[0] = context.app_version_main;
+       regid->id_suffix[1] = context.app_version_sub;
+}
+
+
+/*
+ * Fill in timestamp structure based on clock_gettime(). Time is reported back
+ * as a time_t accompanied with a nano second field.
+ *
+ * The husec, usec and csec could be relaxed in type.
+ */
+static void
+udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp)
+{
+       struct tm tm;
+       uint64_t husec, usec, csec;
+
+       memset(timestamp, 0, sizeof(*timestamp));
+       gmtime_r(&timespec->tv_sec, &tm);
+
+       /*
+        * Time type and time zone : see ECMA 1/7.3, UDF 2., 2.1.4.1, 3.1.1.
+        *
+        * Lower 12 bits are two complement signed timezone offset if bit 12
+        * (method 1) is clear. Otherwise if bit 12 is set, specify timezone
+        * offset to -2047 i.e. unsigned `zero'
+        */
+
+       /* set method 1 for CUT/GMT */
+       timestamp->type_tz      = udf_rw16((1<<12) + 0);
+       timestamp->year         = udf_rw16(tm.tm_year + 1900);
+       timestamp->month        = tm.tm_mon + 1;        /* `tm' uses 0..11 for months */
+       timestamp->day          = tm.tm_mday;
+       timestamp->hour         = tm.tm_hour;
+       timestamp->minute       = tm.tm_min;
+       timestamp->second       = tm.tm_sec;
+
+       usec   = (timespec->tv_nsec + 500) / 1000;      /* round */
+       husec  =   usec / 100;
+       usec  -=  husec * 100;                          /* only 0-99 in usec  */
+       csec   =  husec / 100;                          /* only 0-99 in csec  */
+       husec -=   csec * 100;                          /* only 0-99 in husec */
+
+       /* in rare cases there is overflow in csec */
+       csec  = MIN(99, csec);
+       husec = MIN(99, husec);
+       usec  = MIN(99, usec);
+
+       timestamp->centisec     = csec;
+       timestamp->hund_usec    = husec;
+       timestamp->usec         = usec;
+}
+
+
+void
+udf_set_timestamp_now(struct timestamp *timestamp)
+{
+       struct timespec now;
+
+#ifdef CLOCK_REALTIME
+       (void)clock_gettime(CLOCK_REALTIME, &now);
+#else
+       struct timeval time_of_day;
+
+       (void)gettimeofday(&time_of_day, NULL);
+       now.tv_sec = time_of_day.tv_sec;
+       now.tv_nsec = time_of_day.tv_usec * 1000;
+#endif
+       udf_timespec_to_timestamp(&now, timestamp);
+}
+
+
+/* some code copied from sys/fs/udf */
+
+static void
+udf_set_timestamp(struct timestamp *timestamp, time_t value)
+{
+       struct timespec t;
+
+       memset(&t, 0, sizeof(struct timespec));
+       t.tv_sec  = value;
+       t.tv_nsec = 0;
+       udf_timespec_to_timestamp(&t, timestamp);
+}
+
+
+static uint32_t
+unix_mode_to_udf_perm(mode_t mode)
+{
+       uint32_t perm;
+       
+       perm  = ((mode & S_IRWXO)     );
+       perm |= ((mode & S_IRWXG) << 2);
+       perm |= ((mode & S_IRWXU) << 4);
+       perm |= ((mode & S_IWOTH) << 3);
+       perm |= ((mode & S_IWGRP) << 5);
+       perm |= ((mode & S_IWUSR) << 7);
+
+       return perm;
+}
+
+/* end of copied code */
+
+
+int
+udf_create_primaryd(void)
+{
+       struct pri_vol_desc *pri;
+       uint16_t crclen;
+
+       pri = calloc(1, context.sector_size);
+       if (pri == NULL)
+               return ENOMEM;
+
+       memset(pri, 0, context.sector_size);
+       udf_inittag(&pri->tag, TAGID_PRI_VOL, /* loc */ 0);
+       pri->seq_num = udf_rw32(context.vds_seq); context.vds_seq++;
+
+       pri->pvd_num = udf_rw32(0);             /* default serial */
+       udf_encode_osta_id(pri->vol_id, 32, context.primary_name);
+
+       /* set defaults for single disc volumes as UDF prescribes */
+       pri->vds_num      = udf_rw16(1);
+       pri->max_vol_seq  = udf_rw16(1);
+       pri->ichg_lvl     = udf_rw16(2);
+       pri->max_ichg_lvl = udf_rw16(3);
+       pri->flags        = udf_rw16(0);
+
+       pri->charset_list     = udf_rw32(1);    /* only CS0 */
+       pri->max_charset_list = udf_rw32(1);    /* only CS0 */
+
+       udf_encode_osta_id(pri->volset_id, 128, context.volset_name);
+       udf_osta_charset(&pri->desc_charset);
+       udf_osta_charset(&pri->explanatory_charset);
+
+       udf_set_regid(&pri->app_id, context.app_name);
+       udf_add_app_regid(&pri->app_id);
+
+       udf_set_regid(&pri->imp_id, context.impl_name);
+       udf_add_impl_regid(&pri->imp_id);
+
+       udf_set_timestamp_now(&pri->time);
+
+       crclen = sizeof(struct pri_vol_desc) - UDF_DESC_TAG_LENGTH;
+       pri->tag.desc_crc_len = udf_rw16(crclen);
+
+       context.primary_vol = pri;
+
+       return 0;
+}
+
+
+/* XXX no support for unallocated or freed space tables yet (!) */
+int
+udf_create_partitiond(int part_num, int part_accesstype)
+{
+       struct part_desc     *pd;
+       struct part_hdr_desc *phd;
+       uint32_t sector_size, bitmap_bytes;
+       uint16_t crclen;
+
+       sector_size = context.sector_size;
+       bitmap_bytes = layout.alloc_bitmap_dscr_size * sector_size;
+
+       if (context.partitions[part_num]) {
+               printf("Internal error: partition %d allready defined\n",
+                       part_num);
+               return EINVAL;
+       }
+
+       pd = calloc(1, context.sector_size);
+       if (pd == NULL)
+               return ENOMEM;
+       phd = &pd->_impl_use.part_hdr;
+
+       udf_inittag(&pd->tag, TAGID_PARTITION, /* loc */ 0);
+       pd->seq_num  = udf_rw32(context.vds_seq); context.vds_seq++;
+
+       pd->flags    = udf_rw16(1);             /* allocated */
+       pd->part_num = udf_rw16(part_num);      /* only one physical partition */
+
+       if (context.dscrver == 2) {
+               udf_set_regid(&pd->contents, "+NSR02");
+       } else {
+               udf_set_regid(&pd->contents, "+NSR03");
+       }
+       udf_add_app_regid(&pd->contents);
+
+       phd->unalloc_space_bitmap.len    = udf_rw32(bitmap_bytes);
+       phd->unalloc_space_bitmap.lb_num = udf_rw32(layout.unalloc_space);
+
+       if (layout.freed_space) {
+               phd->freed_space_bitmap.len    = udf_rw32(bitmap_bytes);
+               phd->freed_space_bitmap.lb_num = udf_rw32(layout.freed_space);
+       }
+
+       pd->access_type = udf_rw32(part_accesstype);
+       pd->start_loc   = udf_rw32(layout.part_start_lba);
+       pd->part_len    = udf_rw32(layout.part_size_lba);
+
+       udf_set_regid(&pd->imp_id, context.impl_name);
+       udf_add_impl_regid(&pd->imp_id);
+
+       crclen = sizeof(struct part_desc) - UDF_DESC_TAG_LENGTH;
+       pd->tag.desc_crc_len = udf_rw16(crclen);
+
+       context.partitions[part_num] = pd;
+
+       return 0;
+}
+
+
+int
+udf_create_unalloc_spaced(void)
+{
+       struct unalloc_sp_desc *usd;
+       uint16_t crclen;
+
+       usd = calloc(1, context.sector_size);
+       if (usd == NULL)
+               return ENOMEM;
+
+       udf_inittag(&usd->tag, TAGID_UNALLOC_SPACE, /* loc */ 0);
+       usd->seq_num  = udf_rw32(context.vds_seq); context.vds_seq++;
+
+       /* no default entries */
+       usd->alloc_desc_num = udf_rw32(0);              /* no entries */
+
+       crclen  = sizeof(struct unalloc_sp_desc) - sizeof(struct extent_ad);
+       crclen -= UDF_DESC_TAG_LENGTH;
+       usd->tag.desc_crc_len = udf_rw16(crclen);
+
+       context.unallocated = usd;
+
+       return 0;
+}
+
+
+static int
+udf_create_base_logical_dscr(void)
+{
+       struct logvol_desc *lvd;
+       uint32_t sector_size;
+       uint16_t crclen;
+
+       sector_size = context.sector_size;
+
+       lvd = calloc(1, sector_size);
+       if (lvd == NULL)
+               return ENOMEM;
+
+       udf_inittag(&lvd->tag, TAGID_LOGVOL, /* loc */ 0);
+       lvd->seq_num  = udf_rw32(context.vds_seq); context.vds_seq++;
+
+       udf_osta_charset(&lvd->desc_charset);
+       udf_encode_osta_id(lvd->logvol_id, 128, context.logvol_name);
+       lvd->lb_size = udf_rw32(context.sector_size);
+
+       udf_set_regid(&lvd->domain_id, "*OSTA UDF Compliant");
+       udf_add_domain_regid(&lvd->domain_id);
+
+       /* no partition mappings/entries yet */
+       lvd->mt_l = udf_rw32(0);
+       lvd->n_pm = udf_rw32(0);
+
+       udf_set_regid(&lvd->imp_id, context.impl_name);
+       udf_add_impl_regid(&lvd->imp_id);
+
+       lvd->integrity_seq_loc.loc = udf_rw32(layout.lvis);
+       lvd->integrity_seq_loc.len = udf_rw32(layout.lvis_size * sector_size);
+
+       /* just one fsd for now */
+       lvd->lv_fsd_loc.len = udf_rw32(sector_size);
+       lvd->lv_fsd_loc.loc.part_num = udf_rw32(context.metadata_part);
+       lvd->lv_fsd_loc.loc.lb_num   = udf_rw32(layout.fsd);
+
+       crclen  = sizeof(struct logvol_desc) - 1 - UDF_DESC_TAG_LENGTH;
+       lvd->tag.desc_crc_len = udf_rw16(crclen);
+
+       context.logical_vol = lvd;
+       context.vtop_tp[UDF_VTOP_RAWPART]      = UDF_VTOP_TYPE_RAW;
+       context.vtop_offset[UDF_VTOP_RAWPART] = 0;
+
+       return 0;
+}
+
+
+static void 
+udf_add_logvol_part_physical(uint16_t phys_part)
+{
+       struct logvol_desc *logvol = context.logical_vol;
+       union  udf_pmap *pmap;
+       uint8_t         *pmap_pos;
+       uint16_t crclen;
+       uint32_t pmap1_size, log_part;
+
+       log_part = udf_rw32(logvol->n_pm);
+       pmap_pos = logvol->maps + udf_rw32(logvol->mt_l);
+       pmap1_size = sizeof(struct part_map_1);
+
+       pmap = (union udf_pmap *) pmap_pos;
+       pmap->pm1.type        = 1;
+       pmap->pm1.len         = sizeof(struct part_map_1);
+       pmap->pm1.vol_seq_num = udf_rw16(1);            /* no multi-volume */
+       pmap->pm1.part_num    = udf_rw16(phys_part);
+
+       context.vtop       [log_part] = phys_part;
+       context.vtop_tp    [log_part] = UDF_VTOP_TYPE_PHYS;
+       context.vtop_offset[log_part] = layout.part_start_lba;
+       context.part_size[log_part] = layout.part_size_lba;
+       context.part_free[log_part] = layout.part_size_lba;
+
+       /* increment number of partitions and length */
+       logvol->n_pm = udf_rw32(log_part + 1);
+       logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmap1_size);
+
+       crclen = udf_rw16(logvol->tag.desc_crc_len) + pmap1_size;
+       logvol->tag.desc_crc_len = udf_rw16(crclen);
+}
+
+
+static void
+udf_add_logvol_part_virtual(uint16_t phys_part)
+{
+       union  udf_pmap *pmap;
+       struct logvol_desc *logvol = context.logical_vol;
+       uint8_t *pmap_pos;
+       uint16_t crclen;
+       uint32_t pmapv_size, log_part;
+
+       log_part = udf_rw32(logvol->n_pm);
+       pmap_pos = logvol->maps + udf_rw32(logvol->mt_l);
+       pmapv_size = sizeof(struct part_map_2);
+
+       pmap = (union udf_pmap *) pmap_pos;
+       pmap->pmv.type        = 2;
+       pmap->pmv.len         = pmapv_size;
+
+       udf_set_regid(&pmap->pmv.id, "*UDF Virtual Partition");
+       udf_add_udf_regid(&pmap->pmv.id);
+
+       pmap->pmv.vol_seq_num = udf_rw16(1);            /* no multi-volume */
+       pmap->pmv.part_num    = udf_rw16(phys_part);
+
+       context.vtop       [log_part] = phys_part;
+       context.vtop_tp    [log_part] = UDF_VTOP_TYPE_VIRT;
+       context.vtop_offset[log_part] = context.vtop_offset[phys_part];
+       context.part_size[log_part] = 0xffffffff;
+       context.part_free[log_part] = 0xffffffff;
+
+       /* increment number of partitions and length */
+       logvol->n_pm = udf_rw32(log_part + 1);
+       logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmapv_size);
+
+       crclen = udf_rw16(logvol->tag.desc_crc_len) + pmapv_size;
+       logvol->tag.desc_crc_len = udf_rw16(crclen);
+}
+
+
+/* sparing table size is in bytes */
+static void
+udf_add_logvol_part_sparable(uint16_t phys_part)
+{
+       union  udf_pmap *pmap;
+       struct logvol_desc *logvol = context.logical_vol;
+       uint32_t *st_pos, sparable_bytes, pmaps_size;
+       uint8_t  *pmap_pos, num;
+       uint16_t crclen;
+       uint32_t log_part;
+
+       log_part = udf_rw32(logvol->n_pm);
+       pmap_pos = logvol->maps + udf_rw32(logvol->mt_l);
+       pmaps_size = sizeof(struct part_map_2);
+       sparable_bytes = layout.sparable_area_size * context.sector_size;
+
+       pmap = (union udf_pmap *) pmap_pos;
+       pmap->pms.type        = 2;
+       pmap->pms.len         = pmaps_size;
+
+       udf_set_regid(&pmap->pmv.id, "*UDF Sparable Partition");
+       udf_add_udf_regid(&pmap->pmv.id);
+
+       pmap->pms.vol_seq_num = udf_rw16(1);            /* no multi-volume */
+       pmap->pms.part_num    = udf_rw16(phys_part);
+
+       pmap->pms.packet_len  = udf_rw16(layout.sparable_blockingnr);
+       pmap->pms.st_size     = udf_rw32(sparable_bytes);
+
+       /* enter spare tables  */
+       st_pos = &pmap->pms.st_loc[0];
+       *st_pos++ = udf_rw32(layout.spt_1);
+       *st_pos++ = udf_rw32(layout.spt_2);
+
+       num = 2;
+       if (layout.spt_2 == 0) num--;
+       if (layout.spt_1 == 0) num--;
+       pmap->pms.n_st = num;           /* 8 bit */
+
+       /* the vtop_offset needs to explicitly set since there is no phys. */
+       context.vtop       [log_part] = phys_part;
+       context.vtop_tp    [log_part] = UDF_VTOP_TYPE_SPARABLE;
+       context.vtop_offset[log_part] = layout.part_start_lba;
+       context.part_size[log_part] = layout.part_size_lba;
+       context.part_free[log_part] = layout.part_size_lba;
+
+       /* increment number of partitions and length */
+       logvol->n_pm = udf_rw32(log_part + 1);
+       logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmaps_size);
+
+       crclen = udf_rw16(logvol->tag.desc_crc_len) + pmaps_size;
+       logvol->tag.desc_crc_len = udf_rw16(crclen);
+}
+
+
+int
+udf_create_sparing_tabled(void)
+{
+       struct udf_sparing_table *spt;
+       struct spare_map_entry   *sme;
+       uint32_t loc, cnt;
+       uint32_t crclen;        /* XXX: should be 16; need to detect overflow */
+
+       spt = calloc(context.sector_size, layout.sparing_table_dscr_lbas);
+       if (spt == NULL)
+               return ENOMEM;
+
+       /* a sparing table descriptor is a whole sparable_blockingnr sectors */
+       udf_inittag(&spt->tag, TAGID_SPARING_TABLE, /* loc */ 0);
+
+       udf_set_regid(&spt->id, "*UDF Sparing Table");
+       udf_add_udf_regid(&spt->id);
+
+       spt->rt_l    = udf_rw16(layout.sparable_blocks);
+       spt->seq_num = udf_rw32(0);                     /* first generation */
+
+       for (cnt = 0; cnt < layout.sparable_blocks; cnt++) {
+               sme = &spt->entries[cnt];
+               loc = layout.sparable_area + cnt * layout.sparable_blockingnr;
+               sme->org = udf_rw32(0xffffffff);        /* open for reloc */
+               sme->map = udf_rw32(loc);
+       }
+
+       /* calculate crc len for actual size */
+       crclen  = sizeof(struct udf_sparing_table) - UDF_DESC_TAG_LENGTH;
+       crclen += (layout.sparable_blocks-1) * sizeof(struct spare_map_entry);
+/* XXX ensure crclen doesn't exceed UINT16_MAX ? */
+       spt->tag.desc_crc_len = udf_rw16((uint16_t)crclen);
+
+       context.sparing_table = spt;
+
+       return 0;
+}
+
+
+static void
+udf_add_logvol_part_meta(uint16_t phys_part)
+{
+       union  udf_pmap *pmap;
+       struct logvol_desc *logvol = context.logical_vol;
+       uint8_t *pmap_pos;
+       uint32_t pmapv_size, log_part;
+       uint16_t crclen;
+
+       log_part = udf_rw32(logvol->n_pm);
+       pmap_pos = logvol->maps + udf_rw32(logvol->mt_l);
+       pmapv_size = sizeof(struct part_map_2);
+
+       pmap = (union udf_pmap *) pmap_pos;
+       pmap->pmm.type        = 2;
+       pmap->pmm.len         = pmapv_size;
+
+       udf_set_regid(&pmap->pmm.id, "*UDF Metadata Partition");
+       udf_add_udf_regid(&pmap->pmm.id);
+
+       pmap->pmm.vol_seq_num = udf_rw16(1);            /* no multi-volume */
+       pmap->pmm.part_num    = udf_rw16(phys_part);
+
+       /* fill in meta data file(s) and alloc/alignment unit sizes */
+       pmap->pmm.meta_file_lbn        = udf_rw32(layout.meta_file);
+       pmap->pmm.meta_mirror_file_lbn = udf_rw32(layout.meta_mirror);
+       pmap->pmm.meta_bitmap_file_lbn = udf_rw32(layout.meta_bitmap);
+       pmap->pmm.alloc_unit_size      = udf_rw32(layout.meta_blockingnr);
+       pmap->pmm.alignment_unit_size  = udf_rw16(layout.meta_alignment);
+       pmap->pmm.flags                = 0; /* METADATA_DUPLICATED */
+
+       context.vtop       [log_part] = phys_part;
+       context.vtop_tp    [log_part] = UDF_VTOP_TYPE_META;
+       context.vtop_offset[log_part] =
+               context.vtop_offset[phys_part] + layout.meta_part_start_lba;
+       context.part_size[log_part] = layout.meta_part_size_lba;
+       context.part_free[log_part] = layout.meta_part_size_lba;
+
+       /* increment number of partitions and length */
+       logvol->n_pm = udf_rw32(log_part + 1);
+       logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmapv_size);
+
+       crclen = udf_rw16(logvol->tag.desc_crc_len) + pmapv_size;
+       logvol->tag.desc_crc_len = udf_rw16(crclen);
+}
+
+
+int
+udf_create_logical_dscr(int format_flags)
+{
+       int error;
+
+       if ((error = udf_create_base_logical_dscr()))
+               return error;
+
+       /* we pass data_part for there might be a read-only part one day */
+       if (format_flags & FORMAT_SPARABLE) {
+               /* sparable partition mapping has no physical mapping */
+               udf_add_logvol_part_sparable(context.data_part);
+       } else {
+               udf_add_logvol_part_physical(context.data_part);
+       }
+
+       if (format_flags & FORMAT_VAT) {
+               /* add VAT virtual mapping; reflects on datapart */
+               udf_add_logvol_part_virtual(context.data_part);
+       }
+       if (format_flags & FORMAT_META) {
+               /* add META data mapping; reflects on datapart */
+               udf_add_logvol_part_meta(context.data_part);
+       }
+
+       return 0;
+}
+
+
+int
+udf_create_impvold(char *field1, char *field2, char *field3)
+{
+       struct impvol_desc *ivd;
+       struct udf_lv_info *lvi;
+       uint16_t crclen;
+
+       ivd = calloc(1, context.sector_size);
+       if (ivd == NULL)
+               return ENOMEM;
+       lvi = &ivd->_impl_use.lv_info;
+
+       udf_inittag(&ivd->tag, TAGID_IMP_VOL, /* loc */ 0);
+       ivd->seq_num  = udf_rw32(context.vds_seq); context.vds_seq++;
+
+       udf_set_regid(&ivd->impl_id, "*UDF LV Info");
+       udf_add_udf_regid(&ivd->impl_id);
+
+       /* fill in UDF specific part */
+       udf_osta_charset(&lvi->lvi_charset);
+       udf_encode_osta_id(lvi->logvol_id, 128, context.logvol_name);
+
+       udf_encode_osta_id(lvi->lvinfo1, 36, field1);
+       udf_encode_osta_id(lvi->lvinfo2, 36, field2);
+       udf_encode_osta_id(lvi->lvinfo3, 36, field3);
+
+       udf_set_regid(&lvi->impl_id, context.impl_name);
+       udf_add_impl_regid(&lvi->impl_id);
+
+       crclen  = sizeof(struct impvol_desc) - UDF_DESC_TAG_LENGTH;
+       ivd->tag.desc_crc_len = udf_rw16(crclen);
+
+       context.implementation = ivd;
+
+       return 0;
+}
+
+
+/* XXX might need to be sanitised a bit later */
+void
+udf_update_lvintd(int type)
+{
+       struct logvol_int_desc *lvid;
+       struct udf_logvol_info *lvinfo;
+       struct logvol_desc     *logvol;
+       uint32_t *pos;
+       uint32_t cnt, l_iu, num_partmappings;
+       uint32_t crclen;        /* XXX: should be 16; need to detect overflow */
+
+       lvid   = context.logvol_integrity;
+       logvol = context.logical_vol;
+
+       assert(lvid);
+       assert(logvol);
+
+       lvid->integrity_type = udf_rw32(type);
+
+       num_partmappings = udf_rw32(logvol->n_pm);
+
+       udf_set_timestamp_now(&lvid->time);
+
+       lvinfo = (struct udf_logvol_info *)
+               (lvid->tables + num_partmappings * 2);
+       udf_set_regid(&lvinfo->impl_id, context.impl_name);
+       udf_add_impl_regid(&lvinfo->impl_id);
+
+       lvinfo->num_files          = udf_rw32(context.num_files);
+       lvinfo->num_directories    = udf_rw32(context.num_directories);
+
+       lvid->lvint_next_unique_id = udf_rw64(context.unique_id);
+
+       /* XXX sane enough ? */
+       lvinfo->min_udf_readver  = udf_rw16(context.min_udf);
+       lvinfo->min_udf_writever = udf_rw16(context.min_udf);
+       lvinfo->max_udf_writever = udf_rw16(context.max_udf);
+
+       lvid->num_part = udf_rw32(num_partmappings);
+
+       /* no impl. use needed */
+       l_iu = sizeof(struct udf_logvol_info);
+       lvid->l_iu = udf_rw32(l_iu);
+
+       pos = &lvid->tables[0];
+       for (cnt = 0; cnt < num_partmappings; cnt++) {
+               *pos++ = udf_rw32(context.part_free[cnt]);
+       }
+       for (cnt = 0; cnt < num_partmappings; cnt++) {
+               *pos++ = udf_rw32(context.part_size[cnt]);
+       }
+
+       crclen  = sizeof(struct logvol_int_desc) -4 -UDF_DESC_TAG_LENGTH + l_iu;
+       crclen += num_partmappings * 2 * 4;
+/* XXX ensure crclen doesn't exceed UINT16_MAX ? */
+       lvid->tag.desc_crc_len = udf_rw16(crclen);
+
+       context.logvol_info = lvinfo;
+}
+
+
+int
+udf_create_lvintd(int type)
+{
+       struct logvol_int_desc *lvid;
+
+       lvid = calloc(1, context.sector_size);
+       if (lvid == NULL)
+               return ENOMEM;
+
+       udf_inittag(&lvid->tag, TAGID_LOGVOL_INTEGRITY, /* loc */ 0);
+
+       context.logvol_integrity = lvid;
+
+       udf_update_lvintd(type);
+
+       return 0;
+}
+
+
+int
+udf_create_fsd(void)
+{
+       struct fileset_desc *fsd;
+       uint16_t crclen;
+
+       fsd = calloc(1, context.sector_size);
+       if (fsd == NULL)
+               return ENOMEM;
+
+       udf_inittag(&fsd->tag, TAGID_FSD, /* loc */ 0);
+
+       udf_set_timestamp_now(&fsd->time);
+       fsd->ichg_lvl     = udf_rw16(3);                /* UDF 2.3.2.1 */
+       fsd->max_ichg_lvl = udf_rw16(3);                /* UDF 2.3.2.2 */
+
+       fsd->charset_list     = udf_rw32(1);            /* only CS0 */
+       fsd->max_charset_list = udf_rw32(1);            /* only CS0 */
+
+       fsd->fileset_num      = udf_rw32(0);            /* only one fsd */
+       fsd->fileset_desc_num = udf_rw32(0);            /* origional    */
+
+       udf_osta_charset(&fsd->logvol_id_charset);
+       udf_encode_osta_id(fsd->logvol_id, 128, context.logvol_name);
+
+       udf_osta_charset(&fsd->fileset_charset);
+       udf_encode_osta_id(fsd->fileset_id, 32, context.fileset_name);
+
+       /* copyright file and abstract file names obmitted */
+
+       fsd->rootdir_icb.len          = udf_rw32(context.sector_size);
+       fsd->rootdir_icb.loc.lb_num   = udf_rw32(layout.rootdir);
+       fsd->rootdir_icb.loc.part_num = udf_rw16(context.metadata_part);
+
+       udf_set_regid(&fsd->domain_id, "*OSTA UDF Compliant");
+       udf_add_domain_regid(&fsd->domain_id);
+
+       /* next_ex stays zero */
+       /* no system streamdirs yet */
+
+       crclen = sizeof(struct fileset_desc) - UDF_DESC_TAG_LENGTH;
+       fsd->tag.desc_crc_len = udf_rw16(crclen);
+
+       context.fileset_desc = fsd;
+
+       return 0;
+}
+
+
+int
+udf_create_space_bitmap(uint32_t dscr_size, uint32_t part_size_lba,
+       struct space_bitmap_desc **sbdp)
+{
+       struct space_bitmap_desc *sbd;
+       uint32_t cnt;
+       uint16_t crclen;
+
+       *sbdp = NULL;
+       sbd = calloc(context.sector_size, dscr_size);
+       if (sbd == NULL)
+               return ENOMEM;
+
+       udf_inittag(&sbd->tag, TAGID_SPACE_BITMAP, /* loc */ 0);
+
+       sbd->num_bits  = udf_rw32(part_size_lba);
+       sbd->num_bytes = udf_rw32((part_size_lba + 7)/8);
+
+       /* fill space with 0xff to indicate free */
+       for (cnt = 0; cnt < udf_rw32(sbd->num_bytes); cnt++)
+               sbd->data[cnt] = 0xff;
+
+       /* set crc to only cover the header (UDF 2.3.1.2, 2.3.8.1) */
+       crclen = sizeof(struct space_bitmap_desc) -1 - UDF_DESC_TAG_LENGTH;
+       sbd->tag.desc_crc_len = udf_rw16(crclen);
+
+       *sbdp = sbd;
+       return 0;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+int 
+udf_register_bad_block(uint32_t location)
+{
+       struct udf_sparing_table *spt;
+       struct spare_map_entry   *sme, *free_sme;
+       uint32_t cnt;
+
+       spt = context.sparing_table;
+       if (spt == NULL) {
+               printf("internal error: adding bad block to non sparable\n");
+               return EINVAL;
+       }
+
+       /* find us a free spare map entry */
+       free_sme = NULL;
+       for (cnt = 0; cnt < layout.sparable_blocks; cnt++) {
+               sme = &spt->entries[cnt];
+               /* if we are allready in it, bail out */
+               if (udf_rw32(sme->org) == location)
+                       return 0;
+               if (udf_rw32(sme->org) == 0xffffffff) {
+                       free_sme = sme;
+                       break;
+               }
+       }
+       if (free_sme == NULL) {
+               printf("Disc relocation blocks full; disc too damanged\n");
+               return EINVAL;
+       }
+       free_sme->org = udf_rw32(location);
+
+       return 0;
+}
+
+
+void
+udf_mark_allocated(uint32_t start_lb, int partnr, uint32_t blocks)
+{
+       union dscrptr *dscr;
+       uint8_t *bpos;
+       uint32_t cnt, bit;
+
+       /* account for space used on underlying partition */
+       context.part_free[partnr] -= blocks;
+#ifdef DEBUG
+       printf("mark allocated : partnr %d, start_lb %d for %d blocks\n",
+               partnr, start_lb, blocks);
+#endif
+
+       switch (context.vtop_tp[partnr]) {
+       case UDF_VTOP_TYPE_VIRT:
+               /* nothing */
+               break;
+       case UDF_VTOP_TYPE_PHYS:
+       case UDF_VTOP_TYPE_SPARABLE:
+       case UDF_VTOP_TYPE_META:
+               if (context.part_unalloc_bits[context.vtop[partnr]] == NULL) {
+                       context.part_free[partnr] = 0;
+                       break;
+               }
+#ifdef DEBUG
+               printf("Marking %d+%d as used\n", start_lb, blocks);
+#endif
+               dscr = (union dscrptr *) (context.part_unalloc_bits[partnr]);
+               for (cnt = start_lb; cnt < start_lb + blocks; cnt++) {
+                        bpos  = &dscr->sbd.data[cnt / 8];
+                        bit   = cnt % 8;
+                       *bpos &= ~(1<< bit);
+               }
+               break;
+       default:
+               printf("internal error: reality check in mapping type %d\n",
+                       context.vtop_tp[partnr]);
+               exit(EXIT_FAILURE);
+       }
+}
+
+
+void
+udf_advance_uniqueid(void)
+{
+       /* Minimum value of 16 : UDF 3.2.1.1, 3.3.3.4. */
+       context.unique_id++;
+       if (context.unique_id < 0x10)
+               context.unique_id = 0x10;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void
+unix_to_udf_name(char *result, uint8_t *result_len,
+       char const *name, int name_len, struct charspec *chsp)
+{
+       uint16_t   *raw_name;
+       uint16_t   *outchp;
+       const char *inchp;
+       const char *osta_id = "OSTA Compressed Unicode";
+       int         udf_chars, is_osta_typ0, bits;
+       size_t      cnt;
+
+       /* allocate temporary unicode-16 buffer */
+       raw_name = malloc(1024);
+       assert(raw_name);
+
+       /* convert utf8 to unicode-16 */
+       *raw_name = 0;
+       inchp  = name;
+       outchp = raw_name;
+       bits = 8;
+       for (cnt = name_len, udf_chars = 0; cnt;) {
+               *outchp = wget_utf8(&inchp, &cnt);
+               if (*outchp > 0xff)
+                       bits=16;
+               outchp++;
+               udf_chars++;
+       }
+       /* null terminate just in case */
+       *outchp++ = 0;
+
+       is_osta_typ0  = (chsp->type == 0);
+       is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0);
+       if (is_osta_typ0) {
+               udf_chars = udf_CompressUnicode(udf_chars, bits,
+                               (unicode_t *) raw_name,
+                               (byte *) result);
+       } else {
+               printf("unix to udf name: no CHSP0 ?\n");
+               /* XXX assume 8bit char length byte latin-1 */
+               *result++ = 8; udf_chars = 1;
+               strncpy(result, name + 1, name_len);
+               udf_chars += name_len;
+       }
+       *result_len = udf_chars;
+       free(raw_name);
+}
+
+
+#define UDF_SYMLINKBUFLEN    (64*1024)               /* picked */
+int
+udf_encode_symlink(uint8_t **pathbufp, uint32_t *pathlenp, char *target)
+{
+       struct charspec osta_charspec;
+       struct pathcomp pathcomp;
+       char *pathbuf, *pathpos, *compnamepos;
+//     char *mntonname;
+//     int   mntonnamelen;
+       int pathlen, len, compnamelen;
+       int error;
+
+       /* process `target' to an UDF structure */
+       pathbuf = malloc(UDF_SYMLINKBUFLEN);
+       assert(pathbuf);
+
+       *pathbufp = NULL;
+       *pathlenp = 0;
+
+       pathpos = pathbuf;
+       pathlen = 0;
+       udf_osta_charset(&osta_charspec);
+
+       if (*target == '/') {
+               /* symlink starts from the root */
+               len = UDF_PATH_COMP_SIZE;
+               memset(&pathcomp, 0, len);
+               pathcomp.type = UDF_PATH_COMP_ROOT;
+
+#if 0
+               /* XXX how to check for in makefs? */
+               /* check if its mount-point relative! */
+               mntonname    = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname;
+               mntonnamelen = strlen(mntonname);
+               if (strlen(target) >= mntonnamelen) {
+                       if (strncmp(target, mntonname, mntonnamelen) == 0) {
+                               pathcomp.type = UDF_PATH_COMP_MOUNTROOT;
+                               target += mntonnamelen;
+                       }
+               } else {
+                       target++;
+               }
+#else
+               target++;
+#endif
+
+               memcpy(pathpos, &pathcomp, len);
+               pathpos += len;
+               pathlen += len;
+       }
+
+       error = 0;
+       while (*target) {
+               /* ignore multiple '/' */
+               while (*target == '/') {
+                       target++;
+               }
+               if (!*target)
+                       break;
+
+               /* extract component name */
+               compnamelen = 0;
+               compnamepos = target;
+               while ((*target) && (*target != '/')) {
+                       target++;
+                       compnamelen++;
+               }
+
+               /* just trunc if too long ?? (security issue) */
+               if (compnamelen >= 127) {
+                       error = ENAMETOOLONG;
+                       break;
+               }
+
+               /* convert unix name to UDF name */
+               len = sizeof(struct pathcomp);
+               memset(&pathcomp, 0, len);
+               pathcomp.type = UDF_PATH_COMP_NAME;
+               len = UDF_PATH_COMP_SIZE;
+
+               if ((compnamelen == 2) && (strncmp(compnamepos, "..", 2) == 0))
+                       pathcomp.type = UDF_PATH_COMP_PARENTDIR;
+               if ((compnamelen == 1) && (*compnamepos == '.'))
+                       pathcomp.type = UDF_PATH_COMP_CURDIR;
+
+               if (pathcomp.type == UDF_PATH_COMP_NAME) {
+                       unix_to_udf_name(
+                               (char *) &pathcomp.ident, &pathcomp.l_ci,
+                               compnamepos, compnamelen,
+                               &osta_charspec);
+                       len = UDF_PATH_COMP_SIZE + pathcomp.l_ci;
+               }
+
+               if (pathlen + len >= UDF_SYMLINKBUFLEN) {
+                       error = ENAMETOOLONG;
+                       break;
+               }
+
+               memcpy(pathpos, &pathcomp, len);
+               pathpos += len;
+               pathlen += len;
+       }
+
+       if (error) {
+               /* aparently too big */
+               free(pathbuf);
+               return error;
+       }
+
+       /* return status of symlink contents writeout */
+       *pathbufp = (uint8_t *) pathbuf;
+       *pathlenp = pathlen;
+
+       return 0;
+
+}
+#undef UDF_SYMLINKBUFLEN
+
+
+int
+udf_fidsize(struct fileid_desc *fid)
+{
+       uint32_t size;
+
+       if (udf_rw16(fid->tag.id) != TAGID_FID)
+               errx(EINVAL, "got udf_fidsize on non FID\n");
+
+       size = UDF_FID_SIZE + fid->l_fi + udf_rw16(fid->l_iu);
+       size = (size + 3) & ~3;
+
+       return size;
+}
+
+
+int
+udf_create_parentfid(struct fileid_desc *fid, struct long_ad *parent)
+{
+       /* the size of an empty FID is 38 but needs to be a multiple of 4 */
+       int fidsize = 40;
+
+       udf_inittag(&fid->tag, TAGID_FID, udf_rw32(parent->loc.lb_num));
+       fid->file_version_num = udf_rw16(1);    /* UDF 2.3.4.1 */
+       fid->file_char = UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR;
+       fid->icb = *parent;
+       fid->icb.longad_uniqueid = parent->longad_uniqueid;
+       fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH);
+
+       /* we have to do the fid here explicitly for simplicity */
+       udf_validate_tag_and_crc_sums((union dscrptr *) fid);
+
+       return fidsize;
+}
+
+
+void
+udf_create_fid(uint32_t diroff, struct fileid_desc *fid, char *name,
+       int file_char, struct long_ad *ref)
+{
+       struct charspec osta_charspec;
+       uint32_t endfid;
+       uint32_t fidsize, lb_rest;
+
+       memset(fid, 0, sizeof(*fid));
+       udf_inittag(&fid->tag, TAGID_FID, udf_rw32(ref->loc.lb_num));
+       fid->file_version_num = udf_rw16(1);    /* UDF 2.3.4.1 */
+       fid->file_char = file_char;
+       fid->l_iu = udf_rw16(0);
+       fid->icb = *ref;
+       fid->icb.longad_uniqueid = ref->longad_uniqueid;
+
+       udf_osta_charset(&osta_charspec);
+       unix_to_udf_name((char *) fid->data, &fid->l_fi, name, strlen(name),
+                       &osta_charspec);
+
+       /*
+        * OK, tricky part: we need to pad so the next descriptor header won't
+        * cross the sector boundary
+        */
+       endfid = diroff + udf_fidsize(fid);
+       lb_rest = context.sector_size - (endfid % context.sector_size);
+       if (lb_rest < sizeof(struct desc_tag)) {
+               /* add at least 32 */
+               fid->l_iu = udf_rw16(32);
+               udf_set_regid((struct regid *) fid->data, context.impl_name);
+               udf_add_impl_regid((struct regid *) fid->data);
+
+               unix_to_udf_name((char *) fid->data + udf_rw16(fid->l_iu),
+                       &fid->l_fi, name, strlen(name), &osta_charspec);
+       }
+
+       fidsize = udf_fidsize(fid);
+       fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH);
+
+       /* make sure the header sums stays correct */
+       udf_validate_tag_and_crc_sums((union dscrptr *)fid);
+}
+
+
+static void
+udf_append_parentfid(union dscrptr *dscr, struct long_ad *parent_icb)
+{
+       struct file_entry      *fe;
+       struct extfile_entry   *efe;
+       struct fileid_desc     *fid;
+       uint32_t l_ea;
+       uint32_t fidsize, crclen;
+       uint8_t *bpos, *data;
+
+       fe = NULL;
+       efe = NULL;
+       if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
+               fe    = &dscr->fe;
+               data  = fe->data;
+               l_ea  = udf_rw32(fe->l_ea);
+       } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
+               efe   = &dscr->efe;
+               data  = efe->data;
+               l_ea  = udf_rw32(efe->l_ea);
+       } else {
+               errx(1, "Bad tag passed to udf_append_parentfid");
+       }
+
+       /* create '..' */
+       bpos = data + l_ea;
+       fid  = (struct fileid_desc *) bpos;
+       fidsize = udf_create_parentfid(fid, parent_icb);
+
+       /* record fidlength information */
+       if (fe) {
+               fe->inf_len     = udf_rw64(fidsize);
+               fe->l_ad        = udf_rw32(fidsize);
+               fe->logblks_rec = udf_rw64(0);          /* intern */
+               crclen  = sizeof(struct file_entry);
+       } else {
+               efe->inf_len     = udf_rw64(fidsize);
+               efe->obj_size    = udf_rw64(fidsize);
+               efe->l_ad        = udf_rw32(fidsize);
+               efe->logblks_rec = udf_rw64(0);         /* intern */
+               crclen  = sizeof(struct extfile_entry);
+       }
+       crclen -= 1 + UDF_DESC_TAG_LENGTH;
+       crclen += l_ea + fidsize;
+       dscr->tag.desc_crc_len = udf_rw16(crclen);
+
+       /* make sure the header sums stays correct */
+       udf_validate_tag_and_crc_sums(dscr);
+}
+
+
+
+/*
+ * Order of extended attributes :
+ *   ECMA 167 EAs
+ *   Non block aligned Implementation Use EAs
+ *   Block aligned Implementation Use EAs      (not in newfs_udf)
+ *   Application Use EAs                       (not in newfs_udf)
+ *
+ *   no checks for doubles, must be called in-order
+ */
+static void
+udf_extattr_append_internal(union dscrptr *dscr, struct extattr_entry *extattr)
+{
+       struct file_entry      *fe;
+       struct extfile_entry   *efe;
+       struct extattrhdr_desc *extattrhdr;
+       struct impl_extattr_entry *implext;
+       uint32_t impl_attr_loc, appl_attr_loc, l_ea, a_l, exthdr_len;
+       uint32_t *l_eap, l_ad;
+       uint16_t *spos;
+       uint8_t *bpos, *data;
+
+       if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
+               fe    = &dscr->fe;
+               data  = fe->data;
+               l_eap = &fe->l_ea;
+               l_ad  = udf_rw32(fe->l_ad);
+       } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
+               efe   = &dscr->efe;
+               data  = efe->data;
+               l_eap = &efe->l_ea;
+               l_ad  = udf_rw32(efe->l_ad);
+       } else {
+               errx(1, "Bad tag passed to udf_extattr_append_internal");
+       }
+
+       /* should have a header! */
+       extattrhdr = (struct extattrhdr_desc *) data;
+       l_ea = udf_rw32(*l_eap);
+       if (l_ea == 0) {
+               assert(l_ad == 0);
+               /* create empty extended attribute header */
+               exthdr_len = sizeof(struct extattrhdr_desc);
+
+               udf_inittag(&extattrhdr->tag, TAGID_EXTATTR_HDR, /* loc */ 0);
+               extattrhdr->impl_attr_loc = udf_rw32(exthdr_len);
+               extattrhdr->appl_attr_loc = udf_rw32(exthdr_len);
+               extattrhdr->tag.desc_crc_len = udf_rw16(8);
+
+               /* record extended attribute header length */
+               l_ea = exthdr_len;
+               *l_eap = udf_rw32(l_ea);
+       }
+
+       /* extract locations */
+       impl_attr_loc = udf_rw32(extattrhdr->impl_attr_loc);
+       appl_attr_loc = udf_rw32(extattrhdr->appl_attr_loc);
+       if (impl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT)
+               impl_attr_loc = l_ea;
+       if (appl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT)
+               appl_attr_loc = l_ea;
+
+       /* Ecma 167 EAs */
+       if (udf_rw32(extattr->type) < 2048) {
+               assert(impl_attr_loc == l_ea);
+               assert(appl_attr_loc == l_ea);
+       }
+
+       /* implementation use extended attributes */
+       if (udf_rw32(extattr->type) == 2048) {
+               assert(appl_attr_loc == l_ea);
+
+               /* calculate and write extended attribute header checksum */
+               implext = (struct impl_extattr_entry *) extattr;
+               assert(udf_rw32(implext->iu_l) == 4);   /* [UDF 3.3.4.5] */
+               spos = (uint16_t *) implext->data;
+               *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext));
+       }
+
+       /* application use extended attributes */
+       assert(udf_rw32(extattr->type) != 65536);
+       assert(appl_attr_loc == l_ea);
+
+       /* append the attribute at the end of the current space */
+       bpos = data + udf_rw32(*l_eap);
+       a_l  = udf_rw32(extattr->a_l);
+
+       /* update impl. attribute locations */
+       if (udf_rw32(extattr->type) < 2048) {
+               impl_attr_loc = l_ea + a_l;
+               appl_attr_loc = l_ea + a_l;
+       }
+       if (udf_rw32(extattr->type) == 2048) {
+               appl_attr_loc = l_ea + a_l;
+       }
+
+       /* copy and advance */
+       memcpy(bpos, extattr, a_l);
+       l_ea += a_l;
+       *l_eap = udf_rw32(l_ea);
+
+       /* do the `dance` again backwards */
+       if (context.dscrver != 2) {
+               if (impl_attr_loc == l_ea)
+                       impl_attr_loc = UDF_IMPL_ATTR_LOC_NOT_PRESENT;
+               if (appl_attr_loc == l_ea)
+                       appl_attr_loc = UDF_APPL_ATTR_LOC_NOT_PRESENT;
+       }
+
+       /* store offsets */
+       extattrhdr->impl_attr_loc = udf_rw32(impl_attr_loc);
+       extattrhdr->appl_attr_loc = udf_rw32(appl_attr_loc);
+
+       /* make sure the header sums stays correct */
+       udf_validate_tag_and_crc_sums((union dscrptr *) extattrhdr);
+}
+
+
+int
+udf_create_new_fe(struct file_entry **fep, int file_type, struct stat *st)
+{
+       struct file_entry      *fe;
+       struct icb_tag         *icb;
+       struct timestamp        birthtime;
+       struct filetimes_extattr_entry *ft_extattr;
+       uint32_t crclen;        /* XXX: should be 16; need to detect overflow */
+       uint16_t icbflags;
+
+       *fep = NULL;
+       fe = calloc(1, context.sector_size);
+       if (fe == NULL)
+               return ENOMEM;
+
+       udf_inittag(&fe->tag, TAGID_FENTRY, /* loc */ 0);
+       icb = &fe->icbtag;
+
+       /*
+        * Always use strategy type 4 unless on WORM wich we don't support
+        * (yet). Fill in defaults and set for internal allocation of data.
+        */
+       icb->strat_type      = udf_rw16(4);
+       icb->max_num_entries = udf_rw16(1);
+       icb->file_type       = file_type;       /* 8 bit */
+       icb->flags           = udf_rw16(UDF_ICB_INTERN_ALLOC);
+
+       fe->perm     = udf_rw32(0x7fff);        /* all is allowed   */
+       fe->link_cnt = udf_rw16(0);             /* explicit setting */
+
+       fe->ckpoint  = udf_rw32(1);             /* user supplied file version */
+
+       udf_set_timestamp_now(&birthtime);
+       udf_set_timestamp_now(&fe->atime);
+       udf_set_timestamp_now(&fe->attrtime);
+       udf_set_timestamp_now(&fe->mtime);
+
+       /* set attributes */
+       if (st) {
+#if !HAVE_NBTOOL_CONFIG_H
+               udf_set_timestamp(&birthtime,    st->st_birthtime);
+#else
+               udf_set_timestamp(&birthtime,    0);
+#endif
+               udf_set_timestamp(&fe->atime,    st->st_atime);
+               udf_set_timestamp(&fe->attrtime, st->st_ctime);
+               udf_set_timestamp(&fe->mtime,    st->st_mtime);
+               fe->uid  = udf_rw32(st->st_uid);
+               fe->gid  = udf_rw32(st->st_gid);
+
+               fe->perm = unix_mode_to_udf_perm(st->st_mode);
+
+               icbflags = udf_rw16(fe->icbtag.flags);
+               icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID;
+               icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID;
+               icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY;
+               if (st->st_mode & S_ISUID)
+                       icbflags |= UDF_ICB_TAG_FLAGS_SETUID;
+               if (st->st_mode & S_ISGID)
+                       icbflags |= UDF_ICB_TAG_FLAGS_SETGID;
+               if (st->st_mode & S_ISVTX)
+                       icbflags |= UDF_ICB_TAG_FLAGS_STICKY;
+               fe->icbtag.flags  = udf_rw16(icbflags);
+       }
+
+       udf_set_regid(&fe->imp_id, context.impl_name);
+       udf_add_impl_regid(&fe->imp_id);
+       fe->unique_id = udf_rw64(context.unique_id);
+       udf_advance_uniqueid();
+
+       fe->l_ea = udf_rw32(0);
+
+       /* create extended attribute to record our creation time */
+       ft_extattr = calloc(1, UDF_FILETIMES_ATTR_SIZE(1));
+       ft_extattr->hdr.type = udf_rw32(UDF_FILETIMES_ATTR_NO);
+       ft_extattr->hdr.subtype = 1;    /* [4/48.10.5] */
+       ft_extattr->hdr.a_l = udf_rw32(UDF_FILETIMES_ATTR_SIZE(1));
+       ft_extattr->d_l     = udf_rw32(UDF_TIMESTAMP_SIZE); /* one item */
+       ft_extattr->existence = UDF_FILETIMES_FILE_CREATION;
+       ft_extattr->times[0]  = birthtime;
+
+       udf_extattr_append_internal((union dscrptr *) fe,
+               (struct extattr_entry *) ft_extattr);
+       free(ft_extattr);
+
+       /* record fidlength information */
+       fe->inf_len = udf_rw64(0);
+       fe->l_ad    = udf_rw32(0);
+       fe->logblks_rec = udf_rw64(0);          /* intern */
+
+       crclen  = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH;
+       crclen += udf_rw32(fe->l_ea);
+
+       /* make sure the header sums stays correct */
+       fe->tag.desc_crc_len = udf_rw16(crclen);
+       udf_validate_tag_and_crc_sums((union dscrptr *) fe);
+
+       *fep = fe;
+       return 0;
+}
+
+
+int
+udf_create_new_efe(struct extfile_entry **efep, int file_type, struct stat *st)
+{
+       struct extfile_entry *efe;
+       struct icb_tag       *icb;
+       uint32_t crclen;        /* XXX: should be 16; need to detect overflow */
+       uint16_t icbflags;
+
+       *efep = NULL;
+       efe = calloc(1, context.sector_size);
+       if (efe == NULL)
+               return ENOMEM;
+
+       udf_inittag(&efe->tag, TAGID_EXTFENTRY, /* loc */ 0);
+       icb = &efe->icbtag;
+
+       /*
+        * Always use strategy type 4 unless on WORM wich we don't support
+        * (yet). Fill in defaults and set for internal allocation of data.
+        */
+       icb->strat_type      = udf_rw16(4);
+       icb->max_num_entries = udf_rw16(1);
+       icb->file_type       = file_type;       /* 8 bit */
+       icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC);
+
+       efe->perm     = udf_rw32(0x7fff);       /* all is allowed   */
+       efe->link_cnt = udf_rw16(0);            /* explicit setting */
+
+       efe->ckpoint  = udf_rw32(1);            /* user supplied file version */
+
+       udf_set_timestamp_now(&efe->ctime);
+       udf_set_timestamp_now(&efe->atime);
+       udf_set_timestamp_now(&efe->attrtime);
+       udf_set_timestamp_now(&efe->mtime);
+
+       /* set attributes */
+       if (st) {
+#if !HAVE_NBTOOL_CONFIG_H
+               udf_set_timestamp(&efe->ctime,    st->st_birthtime);
+#else
+               udf_set_timestamp(&efe->ctime,    0);
+#endif
+               udf_set_timestamp(&efe->atime,    st->st_atime);
+               udf_set_timestamp(&efe->attrtime, st->st_ctime);
+               udf_set_timestamp(&efe->mtime,    st->st_mtime);
+               efe->uid = udf_rw32(st->st_uid);
+               efe->gid = udf_rw32(st->st_gid);
+
+               efe->perm = unix_mode_to_udf_perm(st->st_mode);
+
+               icbflags = udf_rw16(efe->icbtag.flags);
+               icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID;
+               icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID;
+               icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY;
+               if (st->st_mode & S_ISUID)
+                       icbflags |= UDF_ICB_TAG_FLAGS_SETUID;
+               if (st->st_mode & S_ISGID)
+                       icbflags |= UDF_ICB_TAG_FLAGS_SETGID;
+               if (st->st_mode & S_ISVTX)
+                       icbflags |= UDF_ICB_TAG_FLAGS_STICKY;
+               efe->icbtag.flags = udf_rw16(icbflags);
+       }
+
+       udf_set_regid(&efe->imp_id, context.impl_name);
+       udf_add_impl_regid(&efe->imp_id);
+
+       efe->unique_id = udf_rw64(context.unique_id);
+       udf_advance_uniqueid();
+
+       /* record fidlength information */
+       efe->inf_len  = udf_rw64(0);
+       efe->obj_size = udf_rw64(0);
+       efe->l_ad     = udf_rw32(0);
+       efe->logblks_rec = udf_rw64(0);
+
+       crclen  = sizeof(struct extfile_entry) - 1 - UDF_DESC_TAG_LENGTH;
+
+       /* make sure the header sums stays correct */
+       efe->tag.desc_crc_len = udf_rw16(crclen);
+       udf_validate_tag_and_crc_sums((union dscrptr *) efe);
+
+       *efep = efe;
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* for METADATA file appending only */
+static void
+udf_append_meta_mapping_part_to_efe(struct extfile_entry *efe,
+               struct short_ad *mapping)
+{
+       struct icb_tag *icb;
+       uint64_t inf_len, obj_size, logblks_rec;
+       uint32_t l_ad, l_ea;
+       uint16_t crclen;
+       uint8_t *bpos;
+
+       inf_len     = udf_rw64(efe->inf_len);
+       obj_size    = udf_rw64(efe->obj_size);
+       logblks_rec = udf_rw64(efe->logblks_rec);
+       l_ad   = udf_rw32(efe->l_ad);
+       l_ea   = udf_rw32(efe->l_ea);
+       crclen = udf_rw16(efe->tag.desc_crc_len);
+       icb    = &efe->icbtag;
+
+       /* set our allocation to shorts if not already done */
+       icb->flags = udf_rw16(UDF_ICB_SHORT_ALLOC);
+
+       /* append short_ad */
+       bpos = (uint8_t *) efe->data + l_ea + l_ad;
+       memcpy(bpos, mapping, sizeof(struct short_ad));
+
+       l_ad   += sizeof(struct short_ad);
+       crclen += sizeof(struct short_ad);
+       inf_len  += UDF_EXT_LEN(udf_rw32(mapping->len));
+       obj_size += UDF_EXT_LEN(udf_rw32(mapping->len));
+       logblks_rec = UDF_ROUNDUP(inf_len, context.sector_size) /
+                               context.sector_size;
+
+       efe->l_ad = udf_rw32(l_ad);
+       efe->inf_len     = udf_rw64(inf_len);
+       efe->obj_size    = udf_rw64(obj_size);
+       efe->logblks_rec = udf_rw64(logblks_rec);
+       efe->tag.desc_crc_len = udf_rw16(crclen);
+}
+
+
+/* for METADATA file appending only */
+static void
+udf_append_meta_mapping_to_efe(struct extfile_entry *efe,
+               uint16_t partnr, uint32_t lb_num,
+       uint64_t len)
+{
+       struct short_ad mapping;
+       uint64_t max_len, part_len;
+
+       /* calculate max length meta allocation sizes */
+       max_len = UDF_EXT_MAXLEN / context.sector_size; /* in sectors */
+       max_len = (max_len / layout.meta_blockingnr) * layout.meta_blockingnr;
+       max_len = max_len * context.sector_size;
+
+       memset(&mapping, 0, sizeof(mapping));
+       while (len) {
+               part_len = MIN(len, max_len);
+               mapping.lb_num   = udf_rw32(lb_num);
+               mapping.len      = udf_rw32(part_len);
+
+               udf_append_meta_mapping_part_to_efe(efe, &mapping);
+
+               lb_num += part_len / context.sector_size;
+               len    -= part_len;
+       }
+}
+
+
+int
+udf_create_meta_files(void)
+{
+       struct extfile_entry *efe;
+       struct long_ad meta_icb;
+       uint64_t bytes;
+       uint32_t sector_size;
+       int filetype, error;
+
+       sector_size = context.sector_size;
+
+       memset(&meta_icb, 0, sizeof(meta_icb));
+       meta_icb.len          = udf_rw32(sector_size);
+       meta_icb.loc.part_num = udf_rw16(context.data_part);
+
+       /* create metadata file */
+       meta_icb.loc.lb_num   = udf_rw32(layout.meta_file);
+       filetype = UDF_ICB_FILETYPE_META_MAIN;
+       error = udf_create_new_efe(&efe, filetype, NULL);
+       if (error)
+               return error;
+       context.meta_file = efe;
+
+       /* create metadata mirror file */
+       meta_icb.loc.lb_num   = udf_rw32(layout.meta_mirror);
+       filetype = UDF_ICB_FILETYPE_META_MIRROR;
+       error = udf_create_new_efe(&efe, filetype, NULL);
+       if (error)
+               return error;
+       context.meta_mirror = efe;
+
+       /* create metadata bitmap file */
+       meta_icb.loc.lb_num   = udf_rw32(layout.meta_bitmap);
+       filetype = UDF_ICB_FILETYPE_META_BITMAP;
+       error = udf_create_new_efe(&efe, filetype, NULL);
+       if (error)
+               return error;
+       context.meta_bitmap = efe;
+
+       /* patch up files */
+       context.meta_file->unique_id   = udf_rw64(0);
+       context.meta_mirror->unique_id = udf_rw64(0);
+       context.meta_bitmap->unique_id = udf_rw64(0);
+
+       /* restart unique id */
+       context.unique_id = 0x10;
+
+       /* XXX no support for metadata mirroring yet */
+       /* insert extents */
+       efe = context.meta_file;
+       udf_append_meta_mapping_to_efe(efe, context.data_part,
+               layout.meta_part_start_lba,
+               (uint64_t) layout.meta_part_size_lba * sector_size);
+
+       efe = context.meta_mirror;
+       udf_append_meta_mapping_to_efe(efe, context.data_part,
+               layout.meta_part_start_lba,
+               (uint64_t) layout.meta_part_size_lba * sector_size);
+
+       efe = context.meta_bitmap;
+       bytes = udf_space_bitmap_len(layout.meta_part_size_lba);
+       udf_append_meta_mapping_to_efe(efe, context.data_part,
+               layout.meta_bitmap_space, bytes);
+
+       return 0;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_create_new_rootdir(union dscrptr **dscr)
+{
+       struct file_entry *fe;
+       struct extfile_entry *efe;
+       struct long_ad root_icb;
+       int filetype, error;
+
+       memset(&root_icb, 0, sizeof(root_icb));
+       root_icb.len          = udf_rw32(context.sector_size);
+       root_icb.loc.lb_num   = udf_rw32(layout.rootdir);
+       root_icb.loc.part_num = udf_rw16(context.metadata_part);
+
+       filetype = UDF_ICB_FILETYPE_DIRECTORY;
+       if (context.dscrver == 2) {
+               error = udf_create_new_fe(&fe, filetype, NULL);
+               *dscr = (union dscrptr *) fe;
+       } else {
+               error = udf_create_new_efe(&efe, filetype, NULL);
+               *dscr = (union dscrptr *) efe;
+       }
+       if (error)
+               return error;
+
+       /* append '..' */
+       udf_append_parentfid(*dscr, &root_icb);
+
+       /* rootdir has explicit only one link on creation; '..' is no link */
+       if (context.dscrver == 2) {
+               fe->link_cnt  = udf_rw16(1);
+       } else {
+               efe->link_cnt = udf_rw16(1);
+       }
+
+       context.num_directories++;
+       assert(context.num_directories == 1);
+
+       return 0;
+}
+
+
+void
+udf_prepend_VAT_file(void)
+{
+       /* old style VAT has no prepend */
+       if (context.dscrver == 2) {
+               context.vat_start = 0;
+               context.vat_size  = 0;
+               return;
+       }
+
+       context.vat_start = offsetof(struct udf_vat, data);
+       context.vat_size  = offsetof(struct udf_vat, data);
+}
+
+
+void
+udf_vat_update(uint32_t virt, uint32_t phys)
+{
+       uint32_t *vatpos;
+       uint32_t new_size;
+
+       if (context.vtop_tp[context.metadata_part] != UDF_VTOP_TYPE_VIRT)
+               return;
+       new_size = MAX(context.vat_size,
+               (context.vat_start + (virt+1)*sizeof(uint32_t)));
+
+       if (new_size > context.vat_allocated) {
+               context.vat_allocated = 
+                       UDF_ROUNDUP(new_size, context.sector_size);
+               context.vat_contents = realloc(context.vat_contents,
+                       context.vat_allocated);
+               assert(context.vat_contents);
+               /* XXX could also report error */
+       }
+       vatpos  = (uint32_t *) (context.vat_contents + context.vat_start);
+       vatpos[virt] = udf_rw32(phys);
+
+       context.vat_size = MAX(context.vat_size,
+               (context.vat_start + (virt+1)*sizeof(uint32_t)));
+}
+
+
+int
+udf_append_VAT_file(void)
+{
+       struct udf_oldvat_tail *oldvat_tail;
+       struct udf_vat *vathdr;
+       int32_t len_diff;
+
+       /* new style VAT has VAT LVInt analog in front */
+       if (context.dscrver == 3) {
+               /* set up VATv2 descriptor */
+               vathdr = (struct udf_vat *) context.vat_contents;
+               vathdr->header_len      = udf_rw16(sizeof(struct udf_vat) - 1);
+               vathdr->impl_use_len    = udf_rw16(0);
+               memcpy(vathdr->logvol_id, context.logical_vol->logvol_id, 128);
+               vathdr->prev_vat        = udf_rw32(UDF_NO_PREV_VAT);
+               vathdr->num_files       = udf_rw32(context.num_files);
+               vathdr->num_directories = udf_rw32(context.num_directories);
+
+               vathdr->min_udf_readver  = udf_rw16(context.min_udf);
+               vathdr->min_udf_writever = udf_rw16(context.min_udf);
+               vathdr->max_udf_writever = udf_rw16(context.max_udf);
+
+               return 0;
+       }
+
+       /* old style VAT has identifier appended */
+
+       /* append "*UDF Virtual Alloc Tbl" id and prev. VAT location */
+       len_diff = context.vat_allocated - context.vat_size;
+       assert(len_diff >= 0);
+       if (len_diff < (int32_t) sizeof(struct udf_oldvat_tail)) {
+               context.vat_allocated += context.sector_size;
+               context.vat_contents = realloc(context.vat_contents,
+                       context.vat_allocated);
+               assert(context.vat_contents);
+               /* XXX could also report error */
+       }
+
+       oldvat_tail = (struct udf_oldvat_tail *) (context.vat_contents +
+                       context.vat_size);
+
+       udf_set_regid(&oldvat_tail->id, "*UDF Virtual Alloc Tbl");
+       udf_add_udf_regid(&oldvat_tail->id);
+       oldvat_tail->prev_vat = udf_rw32(UDF_NO_PREV_VAT);
+
+       context.vat_size += sizeof(struct udf_oldvat_tail);
+
+       return 0;
+}
+
+
+int
+udf_create_VAT(union dscrptr **vat_dscr)
+{
+       struct file_entry *fe;
+       struct extfile_entry *efe;
+       struct impl_extattr_entry *implext;
+       struct vatlvext_extattr_entry *vatlvext;
+       struct long_ad dataloc, *allocpos;
+       uint8_t *bpos, *extattr;
+       uint32_t ea_len, inf_len, vat_len, blks;
+       int filetype;
+       int error;
+
+       assert((layout.rootdir < 2) && (layout.fsd < 2));
+
+       memset(&dataloc, 0, sizeof(dataloc));
+       dataloc.len = udf_rw32(context.vat_size);
+       dataloc.loc.part_num = udf_rw16(context.data_part);
+       dataloc.loc.lb_num   = udf_rw32(layout.vat);
+
+       if (context.dscrver == 2) {
+               /* old style VAT */
+               filetype = UDF_ICB_FILETYPE_UNKNOWN;
+               error = udf_create_new_fe(&fe, filetype, NULL);
+               if (error)
+                       return error;
+
+               /* append VAT LVExtension attribute */
+               ea_len = sizeof(struct impl_extattr_entry) - 1 +
+                        sizeof(struct vatlvext_extattr_entry) + 4;
+
+               extattr = calloc(1, ea_len);
+
+               implext  = (struct impl_extattr_entry *) extattr;
+               implext->hdr.type = udf_rw32(2048);     /* [4/48.10.8] */
+               implext->hdr.subtype = 1;               /* [4/48.10.8.2] */
+               implext->hdr.a_l = udf_rw32(ea_len);    /* VAT LVext EA size */
+               /* use 4 bytes of imp use for UDF checksum [UDF 3.3.4.5] */
+               implext->iu_l = udf_rw32(4);
+               udf_set_regid(&implext->imp_id, "*UDF VAT LVExtension");
+               udf_add_udf_regid(&implext->imp_id);
+
+               /* VAT LVExtension data follows UDF IU space */
+               bpos = ((uint8_t *) implext->data) + 4;
+               vatlvext = (struct vatlvext_extattr_entry *) bpos;
+
+               vatlvext->unique_id_chk = udf_rw64(fe->unique_id);
+               vatlvext->num_files = udf_rw32(context.num_files);
+               vatlvext->num_directories = udf_rw32(context.num_directories);
+               memcpy(vatlvext->logvol_id, context.logical_vol->logvol_id,128);
+
+               udf_extattr_append_internal((union dscrptr *) fe,
+                       (struct extattr_entry *) extattr);
+
+               free(extattr);
+
+               fe->icbtag.flags = udf_rw16(UDF_ICB_LONG_ALLOC);
+
+               allocpos = (struct long_ad *) (fe->data + udf_rw32(fe->l_ea));
+               *allocpos = dataloc;
+
+               /* set length */
+               inf_len = context.vat_size;
+               fe->inf_len = udf_rw64(inf_len);
+               fe->l_ad    = udf_rw32(sizeof(struct long_ad));
+               blks = UDF_ROUNDUP(inf_len, context.sector_size) /
+                       context.sector_size;
+               fe->logblks_rec = udf_rw32(blks);
+
+               /* update vat descriptor's CRC length */
+               vat_len  = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH;
+               vat_len += udf_rw32(fe->l_ad) + udf_rw32(fe->l_ea);
+               fe->tag.desc_crc_len = udf_rw16(vat_len);
+
+               *vat_dscr = (union dscrptr *) fe;
+       } else {
+               /* new style VAT */
+               filetype = UDF_ICB_FILETYPE_VAT;
+               error = udf_create_new_efe(&efe, filetype, NULL);
+               if (error)
+                       return error;
+
+               efe->icbtag.flags = udf_rw16(UDF_ICB_LONG_ALLOC);
+
+               allocpos = (struct long_ad *) efe->data;
+               *allocpos = dataloc;
+
+               /* set length */
+               inf_len = context.vat_size;
+               efe->inf_len     = udf_rw64(inf_len);
+               efe->obj_size    = udf_rw64(inf_len);
+               efe->l_ad        = udf_rw32(sizeof(struct long_ad));
+               blks = UDF_ROUNDUP(inf_len, context.sector_size) /
+                       context.sector_size;
+               efe->logblks_rec = udf_rw32(blks);
+
+               vat_len  = sizeof(struct extfile_entry)-1 - UDF_DESC_TAG_LENGTH;
+               vat_len += udf_rw32(efe->l_ad);
+               efe->tag.desc_crc_len = udf_rw16(vat_len);
+
+               *vat_dscr = (union dscrptr *) efe;
+       }
+       
+       return 0;
+}
+
diff --git a/sbin/newfs_udf/udf_create.h b/sbin/newfs_udf/udf_create.h
new file mode 100644 (file)
index 0000000..001d64e
--- /dev/null
@@ -0,0 +1,283 @@
+/* $NetBSD: udf_create.h,v 1.7 2013/08/09 15:11:08 reinoud Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#ifndef _FS_UDF_UDF_CREATE_H_
+#define _FS_UDF_UDF_CREATE_H_
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#if !HAVE_NBTOOL_CONFIG_H
+#include <fs/udf/ecma167-udf.h>
+#else
+#include "../../sys/fs/udf/ecma167-udf.h"
+#endif
+#include "udf_bswap.h"
+#include "udf_osta.h"
+
+
+/* format flags indicating properties of disc to create */
+#define FORMAT_WRITEONCE       0x00001
+#define FORMAT_SEQUENTIAL      0x00002
+#define FORMAT_REWRITABLE      0x00004
+#define FORMAT_SPARABLE                0x00008
+#define FORMAT_META            0x00010
+#define FORMAT_LOW             0x00020
+#define FORMAT_VAT             0x00040
+#define FORMAT_WORM            0x00080
+#define FORMAT_TRACK512                0x00100
+#define FORMAT_INVALID         0x00200
+#define FORMAT_READONLY                0x00400
+#define FORMAT_FLAGBITS \
+    "\10\1WRITEONCE\2SEQUENTIAL\3REWRITABLE\4SPARABLE\5META\6LOW" \
+    "\7VAT\10WORM\11TRACK512\12INVALID\13READONLY"
+
+
+/* structure space */
+#define UDF_ANCHORS            4       /* 256, 512, N-256, N */
+#define UDF_PARTITIONS         4       /* overkill */
+#define UDF_PMAPS              4       /* overkill */
+
+/* misc constants */
+#define UDF_MAX_NAMELEN                255                     /* as per SPEC */
+
+/* translation constants */
+#define UDF_VTOP_RAWPART UDF_PMAPS     /* [0..UDF_PMAPS> are normal     */
+
+/* virtual to physical mapping types */
+#define UDF_VTOP_TYPE_RAW            0
+#define UDF_VTOP_TYPE_UNKNOWN        0
+#define UDF_VTOP_TYPE_PHYS           1
+#define UDF_VTOP_TYPE_VIRT           2
+#define UDF_VTOP_TYPE_SPARABLE       3
+#define UDF_VTOP_TYPE_META           4
+
+#define UDF_TRANS_ZERO         ((uint64_t) -1)
+#define UDF_TRANS_UNMAPPED     ((uint64_t) -2)
+#define UDF_TRANS_INTERN       ((uint64_t) -3)
+#define UDF_MAX_SECTOR         ((uint64_t) -10)        /* high water mark */
+
+/* handys */
+#define UDF_ROUNDUP(val, gran) \
+       ((uint64_t) (gran) * (((uint64_t)(val) + (gran)-1) / (gran)))
+
+#define UDF_ROUNDDOWN(val, gran) \
+       ((uint64_t) (gran) * (((uint64_t)(val)) / (gran)))
+
+
+/* disc offsets for various structures and their sizes */
+struct udf_disclayout {
+       uint32_t wrtrack_skew;
+
+       uint32_t iso9660_vrs;
+       uint32_t anchors[UDF_ANCHORS];
+       uint32_t vds_size, vds1, vds2;
+       uint32_t lvis_size, lvis;
+
+       uint32_t first_lba, last_lba;
+       uint32_t sector_size;
+       uint32_t blockingnr, align_blockingnr, sparable_blockingnr;
+       uint32_t meta_blockingnr, meta_alignment;
+
+       /* sparables */
+       uint32_t sparable_blocks;
+       uint32_t sparable_area, sparable_area_size;
+       uint32_t sparing_table_dscr_lbas;
+       uint32_t spt_1, spt_2;
+
+       /* bitmaps */
+       uint32_t alloc_bitmap_dscr_size;
+       uint32_t unalloc_space, freed_space;
+
+       uint32_t meta_bitmap_dscr_size;
+       uint32_t meta_bitmap_space;
+
+       /* metadata partition */
+       uint32_t meta_file, meta_mirror, meta_bitmap;
+       uint32_t meta_part_start_lba, meta_part_size_lba;
+
+       /* main partition */
+       uint32_t part_start_lba, part_size_lba;
+
+       uint32_t fsd, rootdir, vat;
+
+};
+
+
+/* all info about discs and descriptors building */
+struct udf_create_context {
+       /* descriptors */
+       int      dscrver;       /* 2 or 3       */
+       int      min_udf;       /* hex          */
+       int      max_udf;       /* hex          */
+       int      serialnum;     /* format serialno */
+
+       int      gmtoff;        /* in minutes   */
+
+       /* XXX to layout? */
+       uint32_t         sector_size;
+
+       /* identification */
+       char    *logvol_name;
+       char    *primary_name;
+       char    *volset_name;
+       char    *fileset_name;
+
+       char const *app_name;
+       char const *impl_name;
+       int      app_version_main;
+       int      app_version_sub;
+
+       /* building */
+       int      vds_seq;       /* for building functions  */
+       int      unique_id;     /* only first few are used */
+
+       /* constructed structures */
+       struct anchor_vdp       *anchors[UDF_ANCHORS];  /* anchors to VDS    */
+       struct pri_vol_desc     *primary_vol;           /* identification    */
+       struct logvol_desc      *logical_vol;           /* main mapping v->p */
+       struct unalloc_sp_desc  *unallocated;           /* free UDF space    */
+       struct impvol_desc      *implementation;        /* likely reduntant  */
+       struct logvol_int_desc  *logvol_integrity;      /* current integrity */
+       struct part_desc        *partitions[UDF_PARTITIONS]; /* partitions   */
+
+       /* XXX to layout? */
+       int     data_part;
+       int     metadata_part;
+
+       /* block numbers as offset in partition */
+       uint32_t metadata_alloc_pos;
+       uint32_t data_alloc_pos;
+
+       /* derived; points *into* other structures */
+       struct udf_logvol_info  *logvol_info;           /* inside integrity  */
+
+       /* fileset and root directories */
+       struct fileset_desc     *fileset_desc;          /* normally one      */
+
+       /* logical to physical translations */
+       int                      vtop[UDF_PMAPS+1];     /* vpartnr trans     */
+       int                      vtop_tp[UDF_PMAPS+1];  /* type of trans     */
+       uint64_t                 vtop_offset[UDF_PMAPS+1]; /* offset in lb   */
+
+       /* sparable */
+       struct udf_sparing_table*sparing_table;         /* replacements      */
+
+       /* VAT file */
+       uint32_t                 vat_size;              /* length */
+       uint32_t                 vat_allocated;         /* allocated length */
+       uint32_t                 vat_start;             /* offset 1st entry */
+       uint8_t                 *vat_contents;          /* the VAT */
+
+       /* meta data partition */
+       struct extfile_entry    *meta_file;
+       struct extfile_entry    *meta_mirror;
+       struct extfile_entry    *meta_bitmap;
+
+       /* lvint */
+       int      num_files;
+       int      num_directories;
+       uint32_t part_size[UDF_PARTITIONS];
+       uint32_t part_free[UDF_PARTITIONS];
+
+       struct space_bitmap_desc*part_unalloc_bits[UDF_PARTITIONS];
+       struct space_bitmap_desc*part_freed_bits  [UDF_PARTITIONS];
+};
+
+
+/* globals */
+
+extern struct udf_create_context context;
+extern struct udf_disclayout     layout;
+
+/* prototypes */
+void udf_init_create_context(void);
+int a_udf_version(const char *s, const char *id_type);
+
+int udf_calculate_disc_layout(int format_flags, int min_udf,
+       uint32_t wrtrack_skew,
+       uint32_t first_lba, uint32_t last_lba,
+       uint32_t sector_size, uint32_t blockingnr,
+       uint32_t sparable_blocks,
+       float meta_fract);
+
+void udf_osta_charset(struct charspec *charspec);
+void udf_encode_osta_id(char *osta_id, uint16_t len, char *text);
+
+void udf_set_regid(struct regid *regid, char const *name);
+void udf_add_domain_regid(struct regid *regid);
+void udf_add_udf_regid(struct regid *regid);
+void udf_add_impl_regid(struct regid *regid);
+void udf_add_app_regid(struct regid *regid);
+
+int  udf_validate_tag_sum(union dscrptr *dscr);
+int  udf_validate_tag_and_crc_sums(union dscrptr *dscr);
+
+void udf_set_timestamp_now(struct timestamp *timestamp);
+
+void udf_inittag(struct desc_tag *tag, int tagid, uint32_t loc);
+int udf_create_anchor(int num);
+
+void udf_create_terminator(union dscrptr *dscr, uint32_t loc);
+int udf_create_primaryd(void);
+int udf_create_partitiond(int part_num, int part_accesstype);
+int udf_create_unalloc_spaced(void);
+int udf_create_sparing_tabled(void);
+int udf_create_space_bitmap(uint32_t dscr_size, uint32_t part_size_lba,
+       struct space_bitmap_desc **sbdp);
+int udf_create_logical_dscr(int format_flags);
+int udf_create_impvold(char *field1, char *field2, char *field3);
+int udf_create_fsd(void);
+int udf_create_lvintd(int type);
+void udf_update_lvintd(int type);
+
+int udf_register_bad_block(uint32_t location);
+void udf_mark_allocated(uint32_t start_lb, int partnr, uint32_t blocks);
+
+int udf_create_new_fe(struct file_entry **fep, int file_type,
+       struct stat *st);
+int udf_create_new_efe(struct extfile_entry **efep, int file_type,
+       struct stat *st);
+
+int udf_encode_symlink(uint8_t **pathbufp, uint32_t *pathlenp, char *target);
+
+void udf_advance_uniqueid(void);
+int udf_fidsize(struct fileid_desc *fid);
+void udf_create_fid(uint32_t diroff, struct fileid_desc *fid,
+       char *name, int namelen, struct long_ad *ref);
+int udf_create_parentfid(struct fileid_desc *fid, struct long_ad *parent);
+
+int udf_create_meta_files(void);
+int udf_create_new_rootdir(union dscrptr **dscr);
+
+int udf_create_VAT(union dscrptr **vat_dscr);
+void udf_prepend_VAT_file(void);
+void udf_vat_update(uint32_t virt, uint32_t phys);
+int udf_append_VAT_file(void);
+
+#endif /* _FS_UDF_UDF_CREATE_H_ */
+
diff --git a/sbin/newfs_udf/udf_write.c b/sbin/newfs_udf/udf_write.c
new file mode 100644 (file)
index 0000000..c4fb4b5
--- /dev/null
@@ -0,0 +1,906 @@
+/* $NetBSD: udf_write.c,v 1.8 2013/08/25 14:13:47 reinoud Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: udf_write.c,v 1.8 2013/08/25 14:13:47 reinoud Exp $");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#define _EXPOSE_MMC
+#include <sys/cdio.h>
+#else
+#include "udf/cdio_mmc_structs.h"
+#endif
+
+#include "udf_create.h"
+#include "udf_write.h"
+#include "newfs_udf.h"
+
+
+union dscrptr *terminator_dscr;
+
+static int
+udf_write_phys(void *blob, uint32_t location, uint32_t sects)
+{
+       uint32_t phys, cnt;
+       uint8_t *bpos;
+       int error;
+
+       for (cnt = 0; cnt < sects; cnt++) {
+               bpos  = (uint8_t *) blob;
+               bpos += context.sector_size * cnt;
+
+               phys = location + cnt;
+               error = udf_write_sector(bpos, phys);
+               if (error)
+                       return error;
+       }
+       return 0;
+}
+
+
+static int
+udf_write_dscr_phys(union dscrptr *dscr, uint32_t location,
+       uint32_t sects)
+{
+       dscr->tag.tag_loc = udf_rw32(location);
+       (void) udf_validate_tag_and_crc_sums(dscr);
+
+       return udf_write_phys(dscr, location, sects);
+}
+
+
+int
+udf_write_dscr_virt(union dscrptr *dscr, uint32_t location, uint32_t vpart,
+       uint32_t sects)
+{
+       struct file_entry *fe;
+       struct extfile_entry *efe;
+       struct extattrhdr_desc *extattrhdr;
+       uint32_t phys;
+
+       extattrhdr = NULL;
+       if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
+               fe = (struct file_entry *) dscr;
+               if (udf_rw32(fe->l_ea) > 0)
+                       extattrhdr = (struct extattrhdr_desc *) fe->data;
+       }
+       if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
+               efe = (struct extfile_entry *) dscr;
+               if (udf_rw32(efe->l_ea) > 0)
+                       extattrhdr = (struct extattrhdr_desc *) efe->data;
+       }
+       if (extattrhdr) {
+               extattrhdr->tag.tag_loc = udf_rw32(location);
+               udf_validate_tag_and_crc_sums((union dscrptr *) extattrhdr);
+       }
+
+       dscr->tag.tag_loc = udf_rw32(location);
+       udf_validate_tag_and_crc_sums(dscr);
+
+       /* determine physical location */
+       phys = context.vtop_offset[vpart];
+       if (context.vtop_tp[vpart] == UDF_VTOP_TYPE_VIRT) {
+               udf_vat_update(location, context.data_alloc_pos);
+               phys += context.data_alloc_pos++;
+       } else {
+               phys += location;
+       }
+
+       return udf_write_phys(dscr, phys, sects);
+}
+
+
+void
+udf_metadata_alloc(int nblk, struct long_ad *pos)
+{
+       memset(pos, 0, sizeof(*pos));
+       pos->len          = udf_rw32(nblk * context.sector_size);
+       pos->loc.lb_num   = udf_rw32(context.metadata_alloc_pos);
+       pos->loc.part_num = udf_rw16(context.metadata_part);
+
+       udf_mark_allocated(context.metadata_alloc_pos, context.metadata_part,
+               nblk);
+
+       context.metadata_alloc_pos += nblk;
+       if (context.metadata_part == context.data_part)
+               context.data_alloc_pos = context.metadata_alloc_pos;
+}
+
+
+void
+udf_data_alloc(int nblk, struct long_ad *pos)
+{
+       memset(pos, 0, sizeof(*pos));
+       pos->len          = udf_rw32(nblk * context.sector_size);
+       pos->loc.lb_num   = udf_rw32(context.data_alloc_pos);
+       pos->loc.part_num = udf_rw16(context.data_part);
+
+       udf_mark_allocated(context.data_alloc_pos, context.data_part, nblk);
+       context.data_alloc_pos += nblk;
+       if (context.metadata_part == context.data_part)
+               context.metadata_alloc_pos = context.data_alloc_pos;
+}
+
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * udf_derive_format derives the format_flags from the disc's mmc_discinfo.
+ * The resulting flags uniquely define a disc format. Note there are at least
+ * 7 distinct format types defined in UDF.
+ */
+
+#define UDF_VERSION(a) \
+       (((a) == 0x100) || ((a) == 0x102) || ((a) == 0x150) || ((a) == 0x200) || \
+        ((a) == 0x201) || ((a) == 0x250) || ((a) == 0x260))
+
+int
+udf_derive_format(int req_enable, int req_disable, int force)
+{
+       /* disc writability, formatted, appendable */
+       if ((mmc_discinfo.mmc_cur & MMC_CAP_RECORDABLE) == 0) {
+               (void)printf("Can't newfs readonly device\n");
+               return EROFS;
+       }
+       if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) {
+               /* sequentials need sessions appended */
+               if (mmc_discinfo.disc_state == MMC_STATE_CLOSED) {
+                       (void)printf("Can't append session to a closed disc\n");
+                       return EROFS;
+               }
+               if ((mmc_discinfo.disc_state != MMC_STATE_EMPTY) && !force) {
+                       (void)printf("Disc not empty! Use -F to force "
+                           "initialisation\n");
+                       return EROFS;
+               }
+       } else {
+               /* check if disc (being) formatted or has been started on */
+               if (mmc_discinfo.disc_state == MMC_STATE_EMPTY) {
+                       (void)printf("Disc is not formatted\n");
+                       return EROFS;
+               }
+       }
+
+       /* determine UDF format */
+       format_flags = 0;
+       if (mmc_discinfo.mmc_cur & MMC_CAP_REWRITABLE) {
+               /* all rewritable media */
+               format_flags |= FORMAT_REWRITABLE;
+               if (context.min_udf >= 0x0250) {
+                       /* standard dictates meta as default */
+                       format_flags |= FORMAT_META;
+               }
+
+               if ((mmc_discinfo.mmc_cur & MMC_CAP_HW_DEFECTFREE) == 0) {
+                       /* sparables for defect management */
+                       if (context.min_udf >= 0x150)
+                               format_flags |= FORMAT_SPARABLE;
+               }
+       } else {
+               /* all once recordable media */
+               format_flags |= FORMAT_WRITEONCE;
+               if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) {
+                       format_flags |= FORMAT_SEQUENTIAL;
+
+                       if (mmc_discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE) {
+                               /* logical overwritable */
+                               format_flags |= FORMAT_LOW;
+                       } else {
+                               /* have to use VAT for overwriting */
+                               format_flags |= FORMAT_VAT;
+                       }
+               } else {
+                       /* rare WORM devices, but BluRay has one, strat4096 */
+                       format_flags |= FORMAT_WORM;
+               }
+       }
+
+       /* enable/disable requests */
+       if (req_disable & FORMAT_META) {
+               format_flags &= ~(FORMAT_META | FORMAT_LOW);
+               req_disable  &= ~FORMAT_META;
+       }
+       if ((format_flags & FORMAT_VAT) & UDF_512_TRACK)
+               format_flags |= FORMAT_TRACK512;
+
+       if (req_enable & FORMAT_READONLY) {
+               format_flags |= FORMAT_READONLY;
+       }
+
+       /* determine partition/media access type */
+       media_accesstype = UDF_ACCESSTYPE_NOT_SPECIFIED;
+       if (mmc_discinfo.mmc_cur & MMC_CAP_REWRITABLE) {
+               media_accesstype = UDF_ACCESSTYPE_OVERWRITABLE;
+               if (mmc_discinfo.mmc_cur & MMC_CAP_ERASABLE)
+                       media_accesstype = UDF_ACCESSTYPE_REWRITEABLE;
+       } else {
+               /* all once recordable media */
+               media_accesstype = UDF_ACCESSTYPE_WRITE_ONCE;
+       }
+       if (mmc_discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE)
+               media_accesstype = UDF_ACCESSTYPE_PSEUDO_OVERWITE;
+
+       /* patch up media accesstype */
+       if (req_enable & FORMAT_READONLY) {
+               /* better now */
+               media_accesstype = UDF_ACCESSTYPE_READ_ONLY;
+       }
+
+       /* adjust minimum version limits */
+       if (format_flags & FORMAT_VAT)
+               context.min_udf = MAX(context.min_udf, 0x0150);
+       if (format_flags & FORMAT_SPARABLE)
+               context.min_udf = MAX(context.min_udf, 0x0150);
+       if (format_flags & FORMAT_META)
+               context.min_udf = MAX(context.min_udf, 0x0250);
+       if (format_flags & FORMAT_LOW)
+               context.min_udf = MAX(context.min_udf, 0x0260);
+
+       /* adjust maximum version limits not to tease or break things */
+       if (!(format_flags & (FORMAT_META | FORMAT_LOW)) &&
+           (context.max_udf > 0x200))
+               context.max_udf = 0x201;
+
+       if ((format_flags & (FORMAT_VAT | FORMAT_SPARABLE)) == 0)
+               if (context.max_udf <= 0x150)
+                       context.min_udf = 0x102;
+
+       /* limit Ecma 167 descriptor if possible/needed */
+       context.dscrver = 3;
+       if ((context.min_udf < 0x200) || (context.max_udf < 0x200)) {
+               context.dscrver = 2;
+               context.max_udf = 0x150;        /* last version < 0x200 */
+       }
+
+       /* is it possible ? */
+       if (context.min_udf > context.max_udf) {
+               (void)printf("Initialisation prohibited by specified maximum "
+                   "UDF version 0x%04x. Minimum version required 0x%04x\n",
+                   context.max_udf, context.min_udf);
+               return EPERM;
+       }
+
+       if (!UDF_VERSION(context.min_udf) || !UDF_VERSION(context.max_udf)) {
+               printf("Choose UDF version numbers from "
+                       "0x102, 0x150, 0x200, 0x201, 0x250 and 0x260\n");
+               printf("Default version is 0x201\n");
+               return EPERM;
+       }
+
+       return 0;
+}
+
+#undef UDF_VERSION
+
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_proces_names(void)
+{
+       uint32_t primary_nr;
+       uint64_t volset_nr;
+
+       if (context.logvol_name == NULL)
+               context.logvol_name = strdup("anonymous");
+       if (context.primary_name == NULL) {
+               if (mmc_discinfo.disc_flags & MMC_DFLAGS_DISCIDVALID) {
+                       primary_nr = mmc_discinfo.disc_id;
+               } else {
+                       primary_nr = (uint32_t) random();
+               }
+               context.primary_name = calloc(32, 1);
+               sprintf(context.primary_name, "%08"PRIx32, primary_nr);
+       }
+       if (context.volset_name == NULL) {
+               if (mmc_discinfo.disc_flags & MMC_DFLAGS_BARCODEVALID) {
+                       volset_nr = mmc_discinfo.disc_barcode;
+               } else {
+                       volset_nr  =  (uint32_t) random();
+                       volset_nr |= ((uint64_t) random()) << 32;
+               }
+               context.volset_name = calloc(128,1);
+               sprintf(context.volset_name, "%016"PRIx64, volset_nr);
+       }
+       if (context.fileset_name == NULL)
+               context.fileset_name = strdup("anonymous");
+
+       /* check passed/created identifiers */
+       if (strlen(context.logvol_name)  > 128) {
+               (void)printf("Logical volume name too long\n");
+               return EINVAL;
+       }
+       if (strlen(context.primary_name) >  32) {
+               (void)printf("Primary volume name too long\n");
+               return EINVAL;
+       }
+       if (strlen(context.volset_name)  > 128) {
+               (void)printf("Volume set name too long\n");
+               return EINVAL;
+       }
+       if (strlen(context.fileset_name) > 32) {
+               (void)printf("Fileset name too long\n");
+               return EINVAL;
+       }
+
+       /* signal all OK */
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_write_iso9660_vrs(void)
+{
+       struct vrs_desc *iso9660_vrs_desc;
+       uint32_t pos;
+       int error, cnt, dpos;
+
+       /* create ISO/Ecma-167 identification descriptors */
+       if ((iso9660_vrs_desc = calloc(1, context.sector_size)) == NULL)
+               return ENOMEM;
+
+       /*
+        * All UDF formats should have their ISO/Ecma-167 descriptors written
+        * except when not possible due to track reservation in the case of
+        * VAT
+        */
+       if ((format_flags & FORMAT_TRACK512) == 0) {
+               dpos = (2048 + context.sector_size - 1) / context.sector_size;
+
+               /* wipe at least 6 times 2048 byte `sectors' */
+               for (cnt = 0; cnt < 6 *dpos; cnt++) {
+                       pos = layout.iso9660_vrs + cnt;
+                       if ((error = udf_write_sector(iso9660_vrs_desc, pos))) {
+                               free(iso9660_vrs_desc);
+                               return error;
+                       }
+               }
+
+               /* common VRS fields in all written out ISO descriptors */
+               iso9660_vrs_desc->struct_type = 0;
+               iso9660_vrs_desc->version     = 1;
+               pos = layout.iso9660_vrs;
+
+               /* BEA01, NSR[23], TEA01 */
+               memcpy(iso9660_vrs_desc->identifier, "BEA01", 5);
+               if ((error = udf_write_sector(iso9660_vrs_desc, pos))) {
+                       free(iso9660_vrs_desc);
+                       return error;
+               }
+               pos += dpos;
+
+               if (context.dscrver == 2)
+                       memcpy(iso9660_vrs_desc->identifier, "NSR02", 5);
+               else
+                       memcpy(iso9660_vrs_desc->identifier, "NSR03", 5);
+               ;
+               if ((error = udf_write_sector(iso9660_vrs_desc, pos))) {
+                       free(iso9660_vrs_desc);
+                       return error;
+               }
+               pos += dpos;
+
+               memcpy(iso9660_vrs_desc->identifier, "TEA01", 5);
+               if ((error = udf_write_sector(iso9660_vrs_desc, pos))) {
+                       free(iso9660_vrs_desc);
+                       return error;
+               }
+       }
+
+       free(iso9660_vrs_desc);
+       /* return success */
+       return 0;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Main function that creates and writes out disc contents based on the
+ * format_flags's that uniquely define the type of disc to create.
+ */
+
+int
+udf_do_newfs_prefix(void)
+{
+       union dscrptr *zero_dscr;
+       union dscrptr *dscr;
+       struct mmc_trackinfo ti;
+       uint32_t sparable_blocks;
+       uint32_t sector_size, blockingnr;
+       uint32_t cnt, loc, len;
+       int sectcopy;
+       int error, integrity_type;
+       int data_part, metadata_part;
+
+       /* init */
+       sector_size = mmc_discinfo.sector_size;
+
+       /* determine span/size */
+       ti.tracknr = mmc_discinfo.first_track_last_session;
+       error = udf_update_trackinfo(&mmc_discinfo, &ti);
+       if (error)
+               return error;
+
+       if (mmc_discinfo.sector_size < context.sector_size) {
+               fprintf(stderr, "Impossible to format: sectorsize too small\n");
+               return EIO;
+       }
+       context.sector_size = sector_size;
+
+       /* determine blockingnr */
+       blockingnr = ti.packet_size;
+       if (blockingnr <= 1) {
+               /* paranoia on blockingnr */
+               switch (mmc_discinfo.mmc_profile) {
+               case 0x08 : /* CDROM */
+               case 0x09 : /* CD-R    */
+               case 0x0a : /* CD-RW   */
+                       blockingnr = 32;        /* UDF requirement */
+                       break;
+               case 0x10 : /* DVDROM */
+               case 0x11 : /* DVD-R (DL) */
+               case 0x12 : /* DVD-RAM */
+               case 0x1b : /* DVD+R      */
+               case 0x2b : /* DVD+R Dual layer */
+               case 0x13 : /* DVD-RW restricted overwrite */
+               case 0x14 : /* DVD-RW sequential */
+                       blockingnr = 16;        /* SCSI definition */
+                       break;
+               case 0x40 : /* BDROM */
+               case 0x41 : /* BD-R Sequential recording (SRM) */
+               case 0x42 : /* BD-R Random recording (RRM) */
+               case 0x43 : /* BD-RE */
+               case 0x51 : /* HD DVD-R   */
+               case 0x52 : /* HD DVD-RW  */
+                       blockingnr = 32;        /* SCSI definition */
+                       break;
+               default:
+                       break;
+               }
+       }
+       if (blockingnr <= 0) {
+               printf("Can't fixup blockingnumber for device "
+                       "type %d\n", mmc_discinfo.mmc_profile);
+
+               printf("Device is not returning valid blocking"
+                       " number and media type is unknown.\n");
+
+               return EINVAL;
+       }
+       wrtrack_skew = ti.track_start % blockingnr;
+
+       if (mmc_discinfo.mmc_class == MMC_CLASS_CD) {
+               /* not too much for CD-RW, still 20MiB */
+               sparable_blocks = 32;
+       } else {
+               /* take a value for DVD*RW mainly, BD is `defect free' */
+               sparable_blocks = 512;
+       }
+
+       /* get layout */
+       error = udf_calculate_disc_layout(format_flags, context.min_udf,
+               wrtrack_skew,
+               ti.track_start, mmc_discinfo.last_possible_lba,
+               context.sector_size, blockingnr, sparable_blocks,
+               meta_fract);
+
+       /* cache partition for we need it often */
+       data_part     = context.data_part;
+       metadata_part = context.metadata_part;
+
+       /* Create sparing table descriptor if applicable */
+       if (format_flags & FORMAT_SPARABLE) {
+               if ((error = udf_create_sparing_tabled()))
+                       return error;
+
+               if (check_surface) {
+                       if ((error = udf_surface_check()))
+                               return error;
+               }
+       }
+
+       /* Create a generic terminator descriptor (later reused) */
+       terminator_dscr = calloc(1, sector_size);
+       if (terminator_dscr == NULL)
+               return ENOMEM;
+       udf_create_terminator(terminator_dscr, 0);
+
+       /*
+        * Start with wipeout of VRS1 upto start of partition. This allows
+        * formatting for sequentials with the track reservation and it 
+        * cleans old rubbish on rewritables. For sequentuals without the
+        * track reservation all is wiped from track start.
+        */
+       if ((zero_dscr = calloc(1, context.sector_size)) == NULL)
+               return ENOMEM;
+
+       loc = (format_flags & FORMAT_TRACK512) ? layout.vds1 : ti.track_start;
+       for (; loc < layout.part_start_lba; loc++) {
+               if ((error = udf_write_sector(zero_dscr, loc))) {
+                       free(zero_dscr);
+                       return error;
+               }
+       }
+       free(zero_dscr);
+
+       /* Create anchors */
+       for (cnt = 0; cnt < 3; cnt++) {
+               if ((error = udf_create_anchor(cnt))) {
+                       return error;
+               }
+       }
+
+       /* 
+        * Create the two Volume Descriptor Sets (VDS) each containing the
+        * following descriptors : primary volume, partition space,
+        * unallocated space, logical volume, implementation use and the
+        * terminator
+        */
+
+       /* start of volume recognision sequence building */
+       context.vds_seq = 0;
+
+       /* Create primary volume descriptor */
+       if ((error = udf_create_primaryd()))
+               return error;
+
+       /* Create partition descriptor */
+       if ((error = udf_create_partitiond(context.data_part, media_accesstype)))
+               return error;
+
+       /* Create unallocated space descriptor */
+       if ((error = udf_create_unalloc_spaced()))
+               return error;
+
+       /* Create logical volume descriptor */
+       if ((error = udf_create_logical_dscr(format_flags)))
+               return error;
+
+       /* Create implementation use descriptor */
+       /* TODO input of fields 1,2,3 and passing them */
+       if ((error = udf_create_impvold(NULL, NULL, NULL)))
+               return error;
+
+       /* write out what we've created so far */
+
+       /* writeout iso9660 vrs */
+       if ((error = udf_write_iso9660_vrs()))
+               return error;
+
+       /* Writeout anchors */
+       for (cnt = 0; cnt < 3; cnt++) {
+               dscr = (union dscrptr *) context.anchors[cnt];
+               loc  = layout.anchors[cnt];
+               if ((error = udf_write_dscr_phys(dscr, loc, 1)))
+                       return error;
+
+               /* sequential media has only one anchor */
+               if (format_flags & FORMAT_SEQUENTIAL)
+                       break;
+       }
+
+       /* write out main and secondary VRS */
+       for (sectcopy = 1; sectcopy <= 2; sectcopy++) {
+               loc = (sectcopy == 1) ? layout.vds1 : layout.vds2;
+
+               /* primary volume descriptor */
+               dscr = (union dscrptr *) context.primary_vol;
+               error = udf_write_dscr_phys(dscr, loc, 1);
+               if (error)
+                       return error;
+               loc++;
+
+               /* partition descriptor(s) */
+               for (cnt = 0; cnt < UDF_PARTITIONS; cnt++) {
+                       dscr = (union dscrptr *) context.partitions[cnt];
+                       if (dscr) {
+                               error = udf_write_dscr_phys(dscr, loc, 1);
+                               if (error)
+                                       return error;
+                               loc++;
+                       }
+               }
+
+               /* unallocated space descriptor */
+               dscr = (union dscrptr *) context.unallocated;
+               error = udf_write_dscr_phys(dscr, loc, 1);
+               if (error)
+                       return error;
+               loc++;
+
+               /* logical volume descriptor */
+               dscr = (union dscrptr *) context.logical_vol;
+               error = udf_write_dscr_phys(dscr, loc, 1);
+               if (error)
+                       return error;
+               loc++;
+
+               /* implementation use descriptor */
+               dscr = (union dscrptr *) context.implementation;
+               error = udf_write_dscr_phys(dscr, loc, 1);
+               if (error)
+                       return error;
+               loc++;
+
+               /* terminator descriptor */
+               error = udf_write_dscr_phys(terminator_dscr, loc, 1);
+               if (error)
+                       return error;
+               loc++;
+       }
+
+       /* writeout the two sparable table descriptors (if needed) */
+       if (format_flags & FORMAT_SPARABLE) {
+               for (sectcopy = 1; sectcopy <= 2; sectcopy++) {
+                       loc  = (sectcopy == 1) ? layout.spt_1 : layout.spt_2;
+                       dscr = (union dscrptr *) context.sparing_table;
+                       len  = layout.sparing_table_dscr_lbas;
+
+                       /* writeout */
+                       error = udf_write_dscr_phys(dscr, loc, len);
+                       if (error)
+                               return error;
+               }
+       }
+
+       /*
+        * Create unallocated space bitmap descriptor. Sequential recorded
+        * media report their own free/used space; no free/used space tables
+        * should be recorded for these.
+        */
+       if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) {
+               error = udf_create_space_bitmap(
+                               layout.alloc_bitmap_dscr_size,
+                               layout.part_size_lba,
+                               &context.part_unalloc_bits[data_part]);
+               if (error)
+                       return error;
+               /* TODO: freed space bitmap if applicable */
+
+               /* mark space allocated for the unallocated space bitmap */
+               udf_mark_allocated(layout.unalloc_space, data_part,
+                       layout.alloc_bitmap_dscr_size);
+       }
+
+       /*
+        * Create metadata partition file entries and allocate and init their
+        * space and free space maps.
+        */
+       if (format_flags & FORMAT_META) {
+               error = udf_create_space_bitmap(
+                               layout.meta_bitmap_dscr_size,
+                               layout.meta_part_size_lba,
+                               &context.part_unalloc_bits[metadata_part]);
+               if (error)
+                       return error;
+       
+               error = udf_create_meta_files();
+               if (error)
+                       return error;
+
+               /* mark space allocated for meta partition and its bitmap */
+               udf_mark_allocated(layout.meta_file,   data_part, 1);
+               udf_mark_allocated(layout.meta_mirror, data_part, 1);
+               udf_mark_allocated(layout.meta_bitmap, data_part, 1);
+               udf_mark_allocated(layout.meta_part_start_lba, data_part,
+                       layout.meta_part_size_lba);
+
+               /* mark space allocated for the unallocated space bitmap */
+               udf_mark_allocated(layout.meta_bitmap_space, data_part,
+                       layout.meta_bitmap_dscr_size);
+       }
+
+       /* create logical volume integrity descriptor */
+       context.num_files = 0;
+       context.num_directories = 0;
+       integrity_type = UDF_INTEGRITY_OPEN;
+       if ((error = udf_create_lvintd(integrity_type)))
+               return error;
+
+       /* writeout initial open integrity sequence + terminator */
+       loc = layout.lvis;
+       dscr = (union dscrptr *) context.logvol_integrity;
+       error = udf_write_dscr_phys(dscr, loc, 1);
+       if (error)
+               return error;
+       loc++;
+       error = udf_write_dscr_phys(terminator_dscr, loc, 1);
+       if (error)
+               return error;
+
+       /* create VAT if needed */
+       if (format_flags & FORMAT_VAT) {
+               context.vat_allocated = context.sector_size;
+               context.vat_contents  = malloc(context.vat_allocated);
+               assert(context.vat_contents);
+
+               udf_prepend_VAT_file();
+       }
+
+       /* create FSD and writeout */
+       if ((error = udf_create_fsd()))
+               return error;
+       udf_mark_allocated(layout.fsd, metadata_part, 1);
+
+       dscr = (union dscrptr *) context.fileset_desc;
+       error = udf_write_dscr_virt(dscr, layout.fsd, metadata_part, 1);
+
+       return error;
+}
+
+
+/* specific routine for newfs to create empty rootdirectory */
+int
+udf_do_rootdir(void) {
+       union dscrptr *root_dscr;
+       int error;
+
+       /* create root directory and write out */
+       assert(context.unique_id == 0x10);
+       context.unique_id = 0;
+       if ((error = udf_create_new_rootdir(&root_dscr)))
+               return error;
+       udf_mark_allocated(layout.rootdir, context.metadata_part, 1);
+
+       error = udf_write_dscr_virt(root_dscr,
+               layout.rootdir, context.metadata_part, 1);
+
+       free(root_dscr);
+
+       return error;
+}
+
+
+int
+udf_do_newfs_postfix(void)
+{
+       union dscrptr *vat_dscr;
+       union dscrptr *dscr;
+       struct long_ad vatdata_pos;
+       uint32_t loc, len, phys, sects;
+       int data_part, metadata_part;
+       int error;
+
+       /* cache partition for we need it often */
+       data_part     = context.data_part;
+       metadata_part = context.metadata_part;
+
+       if ((format_flags & FORMAT_SEQUENTIAL) == 0) {
+               /* update lvint and mark it closed */
+               udf_update_lvintd(UDF_INTEGRITY_CLOSED);
+
+               /* overwrite initial terminator */
+               loc = layout.lvis+1;
+               dscr = (union dscrptr *) context.logvol_integrity;
+               error = udf_write_dscr_phys(dscr, loc, 1);
+               if (error)
+                       return error;
+               loc++;
+       
+               /* mark end of integrity desciptor sequence again */
+               error = udf_write_dscr_phys(terminator_dscr, loc, 1);
+               if (error)
+                       return error;
+       }
+
+       /* write out unallocated space bitmap on non sequential media */
+       if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) {
+               /* writeout unallocated space bitmap */
+               loc  = layout.unalloc_space;
+               dscr = (union dscrptr *) (context.part_unalloc_bits[data_part]);
+               len  = layout.alloc_bitmap_dscr_size;
+               error = udf_write_dscr_virt(dscr, loc, data_part, len);
+               if (error)
+                       return error;
+       }
+
+       if (format_flags & FORMAT_META) {
+               loc = layout.meta_file;
+               dscr = (union dscrptr *) context.meta_file;
+               error = udf_write_dscr_virt(dscr, loc, data_part, 1);
+               if (error)
+                       return error;
+       
+               loc = layout.meta_mirror;
+               dscr = (union dscrptr *) context.meta_mirror;
+               error = udf_write_dscr_virt(dscr, loc, data_part, 1);
+               if (error)
+                       return error;
+
+               loc = layout.meta_bitmap;
+               dscr = (union dscrptr *) context.meta_bitmap;
+               error = udf_write_dscr_virt(dscr, loc, data_part, 1);
+               if (error)
+                       return error;
+
+               /* writeout unallocated space bitmap */
+               loc  = layout.meta_bitmap_space;
+               dscr = (union dscrptr *)
+                       (context.part_unalloc_bits[metadata_part]);
+               len  = layout.meta_bitmap_dscr_size;
+               error = udf_write_dscr_virt(dscr, loc, data_part, len);
+               if (error)
+                       return error;
+       }
+
+       /* create a VAT and account for FSD+root */
+       vat_dscr = NULL;
+       if (format_flags & FORMAT_VAT) {
+               /* update lvint to reflect the newest values (no writeout) */
+               udf_update_lvintd(UDF_INTEGRITY_CLOSED);
+
+               error = udf_append_VAT_file();
+               if (error)
+                       return error;
+
+               /* write out VAT data */
+               sects = UDF_ROUNDUP(context.vat_size, context.sector_size) /
+                       context.sector_size;
+               layout.vat = context.data_alloc_pos;
+               udf_data_alloc(sects, &vatdata_pos);
+
+               loc = udf_rw32(vatdata_pos.loc.lb_num);
+               phys = context.vtop_offset[context.data_part] + loc;
+
+               error = udf_write_phys(context.vat_contents, phys, sects);
+               if (error)
+                       return error;
+               loc += sects;
+
+               /* create new VAT descriptor */
+               error = udf_create_VAT(&vat_dscr);
+               if (error)
+                       return error;
+               context.data_alloc_pos++;
+               loc++;
+
+               error = udf_write_dscr_virt(vat_dscr, loc, metadata_part, 1);
+               free(vat_dscr);
+               if (error)
+                       return error;
+       }
+
+       /* done */
+       return 0;
+}
diff --git a/sbin/newfs_udf/udf_write.h b/sbin/newfs_udf/udf_write.h
new file mode 100644 (file)
index 0000000..d2fd8ac
--- /dev/null
@@ -0,0 +1,54 @@
+/* $NetBSD: udf_write.h,v 1.4 2013/08/05 20:52:08 reinoud Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#ifndef _FS_UDF_UDF_WRITE_H_
+#define _FS_UDF_UDF_WRITE_H_
+
+#include "udf_create.h"
+#if !HAVE_NBTOOL_CONFIG_H
+#define _EXPOSE_MMC
+#include <sys/cdio.h>
+#else
+#include "udf/cdio_mmc_structs.h"
+#endif
+
+/* prototypes */
+
+int udf_write_dscr_virt(union dscrptr *dscr, uint32_t location, uint32_t vpart,
+       uint32_t sects);
+void udf_metadata_alloc(int nblk, struct long_ad *pos);
+void udf_data_alloc(int nblk, struct long_ad *pos);
+
+int udf_derive_format(int req_enable, int req_disable, int force);
+int udf_proces_names(void);
+
+int udf_do_newfs_prefix(void);
+int udf_do_rootdir(void);
+int udf_do_newfs_postfix(void);
+
+#endif /* _FS_UDF_UDF_WRITE_H_ */
diff --git a/sbin/newfs_udf/unicode.h b/sbin/newfs_udf/unicode.h
new file mode 100644 (file)
index 0000000..f54f320
--- /dev/null
@@ -0,0 +1,161 @@
+/* $NetBSD: unicode.h,v 1.1 2013/08/05 14:11:30 reinoud Exp $ */
+
+/*-
+ * Copyright (c) 2001, 2004 The NetBSD Foundation, Inc.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Paul Borman at Krystal Technologies.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+/*
+ * Routines for handling Unicode encoded in UTF-8 form, code derived from
+ * src/lib/libc/locale/utf2.c.
+ */
+static u_int16_t wget_utf8(const char **, size_t *) __unused;
+static int wput_utf8(char *, size_t, u_int16_t) __unused;
+
+/*
+ * Read one UTF8-encoded character off the string, shift the string pointer
+ * and return the character.
+ */
+static u_int16_t
+wget_utf8(const char **str, size_t *sz)
+{
+       unsigned int c;
+       u_int16_t rune = 0;
+       const char *s = *str;
+       static const int _utf_count[16] = {
+               1, 1, 1, 1, 1, 1, 1, 1,
+               0, 0, 0, 0, 2, 2, 3, 0,
+       };
+
+       /* must be called with at least one byte remaining */
+       assert(*sz > 0);
+
+       c = _utf_count[(s[0] & 0xf0) >> 4];
+       if (c == 0 || c > *sz) {
+    decoding_error:
+               /*
+                * The first character is in range 128-255 and doesn't
+                * mark valid a valid UTF-8 sequence. There is not much
+                * we can do with this, so handle by returning
+                * the first character as if it would be a correctly
+                * encoded ISO-8859-1 character.
+                */
+               c = 1;
+       }
+
+       switch (c) {
+       case 1:
+               rune = s[0] & 0xff;
+               break;
+       case 2:
+               if ((s[1] & 0xc0) != 0x80)
+                       goto decoding_error;
+               rune = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
+               break;
+       case 3:
+               if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80)
+                       goto decoding_error;
+               rune = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6)
+                   | (s[2] & 0x3F);
+               break;
+       }
+
+       *str += c;
+       *sz -= c;
+       return rune;
+}
+
+/*
+ * Encode wide character and write it to the string. 'n' specifies
+ * how much buffer space remains in 's'. Returns number of bytes written
+ * to the target string 's'.
+ */
+static int
+wput_utf8(char *s, size_t n, u_int16_t wc)
+{
+       if (wc & 0xf800) {
+               if (n < 3) {
+                       /* bound check failure */
+                       return 0;
+               }
+
+               s[0] = 0xE0 | (wc >> 12);
+               s[1] = 0x80 | ((wc >> 6) & 0x3F);
+               s[2] = 0x80 | ((wc) & 0x3F);
+               return 3;
+       } else if (wc & 0x0780) {
+               if (n < 2) {
+                       /* bound check failure */
+                       return 0;
+               }
+
+               s[0] = 0xC0 | (wc >> 6);
+               s[1] = 0x80 | ((wc) & 0x3F);
+               return 2;
+       } else {
+               if (n < 1) {
+                       /* bound check failure */
+                       return 0;
+               }
+
+               s[0] = wc;
+               return 1;
+       }
+}
diff --git a/sbin/newfs_v7fs/Makefile b/sbin/newfs_v7fs/Makefile
new file mode 100644 (file)
index 0000000..961a05c
--- /dev/null
@@ -0,0 +1,21 @@
+# $NetBSD: Makefile,v 1.4 2012/09/05 23:01:42 riz Exp $
+
+.include <bsd.own.mk>
+
+V7FS = ${NETBSDSRCDIR}/sys/fs/v7fs
+PROG=  newfs_v7fs
+MAN=   newfs_v7fs.8
+SRCS= newfs_v7fs.c main.c v7fs_endian.c v7fs_superblock.c v7fs_inode.c \
+v7fs_datablock.c v7fs_dirent.c v7fs_io.c v7fs_io_user.c progress.c
+
+# use progress meter.
+FSCK=  ${NETBSDSRCDIR}/sbin/fsck
+
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+CPPFLAGS+=-DV7FS_EI -I${V7FS} -I${FSCK}
+.PATH: ${V7FS} ${FSCK}
+
+COPTS.newfs_v7fs.c+=   -Wno-pointer-sign
+
+.include <bsd.prog.mk>
diff --git a/sbin/newfs_v7fs/main.c b/sbin/newfs_v7fs/main.c
new file mode 100644 (file)
index 0000000..a31ebde
--- /dev/null
@@ -0,0 +1,316 @@
+/*     $NetBSD: main.c,v 1.10 2011/08/10 11:31:49 uch Exp $    */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: main.c,v 1.10 2011/08/10 11:31:49 uch Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <err.h>
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_endian.h"
+#include "v7fs_superblock.h"
+#include "v7fs_inode.h"
+#include "v7fs_datablock.h" /*v7fs_datablock_expand/last */
+#include "newfs_v7fs.h"
+#include "progress.h" /*../sbin/fsck */
+
+#define        VPRINTF(lv, fmt, args...)       { if (v7fs_newfs_verbose >= lv) \
+       printf(fmt, ##args); }
+
+static v7fs_daddr_t
+determine_ilist_size(v7fs_daddr_t volume_size, int32_t files)
+{
+       v7fs_daddr_t ilist_size;
+
+       if (files)
+               ilist_size =  howmany(files, V7FS_INODE_PER_BLOCK);
+       else
+               ilist_size = volume_size / 25; /* 4% */
+       if (ilist_size > (v7fs_daddr_t)V7FS_ILISTBLK_MAX)
+               ilist_size = V7FS_ILISTBLK_MAX;
+
+       return ilist_size;
+}
+
+static int
+partition_check(struct v7fs_self *fs)
+{
+       struct v7fs_superblock *sb = &fs->superblock;
+       int error;
+
+       if ((error = v7fs_superblock_load(fs))) {
+               if (error != EINVAL) {
+                       /* Invalid superblock information is OK. */
+                       warnx("Can't read superblock sector.");
+               }
+       }
+       sb->modified = 1;
+       if ((error = v7fs_superblock_writeback(fs))) {
+               if (errno == EROFS) {
+                       warnx("Overwriting disk label? ");
+               }
+               warnx("Can't write superblock sector.");
+       }
+
+       return error;
+}
+
+static int
+make_root(struct v7fs_self *fs)
+{
+       struct v7fs_inode inode;
+       struct v7fs_dirent *dir;
+       int error;
+
+       /* INO 1 badblk (don't used) */
+       memset(&inode, 0, sizeof(inode));
+       inode.inode_number = 1;
+       inode.mode = V7FS_IFREG;        /* V7 manner */
+       v7fs_inode_writeback(fs, &inode);
+
+       /* INO 2 root */
+       v7fs_ino_t ino;
+       if ((error = v7fs_inode_allocate(fs, &ino))) {
+               errno = error;
+               warn("Can't allocate / inode");
+               return error;
+       }
+
+       memset(&inode, 0, sizeof(inode));
+       inode.inode_number = ino;
+       inode.mode = 0777 | V7FS_IFDIR;
+       inode.uid = 0;
+       inode.gid = 0;
+       inode.nlink = 2;        /* . + .. */
+       inode.atime = inode.mtime = inode.ctime = time(0);
+
+       /* root dirent. */
+       v7fs_datablock_expand(fs, &inode, sizeof(*dir) * 2);
+       v7fs_daddr_t blk = inode.addr[0];
+       void *buf;
+       if (!(buf = scratch_read(fs, blk))) {
+               v7fs_inode_deallocate(fs, ino);
+               errno = error = EIO;
+               warn("Can't read / dirent.");
+               return error;
+       }
+       dir = (struct v7fs_dirent *)buf; /*disk endian */
+
+       strcpy(dir[0].name, ".");
+       dir[0].inode_number = V7FS_VAL16(fs, ino);
+       strcpy(dir[1].name, "..");
+       dir[1].inode_number = V7FS_VAL16(fs, ino);
+       if (!fs->io.write(fs->io.cookie, buf, blk)) {/*writeback */
+               scratch_free(fs, buf);
+               errno = error = EIO;
+               warn("Can't write / dirent.");
+               return error;
+       }
+       scratch_free(fs, buf);
+       v7fs_inode_writeback(fs, &inode);
+       if ((error = v7fs_superblock_writeback(fs))) {
+               errno = error;
+               warnx("Can't write superblock.");
+       }
+
+       return error;
+}
+
+static v7fs_daddr_t
+make_freeblocklist(struct v7fs_self *fs, v7fs_daddr_t listblk, uint8_t *buf)
+{
+       uint32_t (*val32)(uint32_t) = fs->val.conv32;
+       uint16_t (*val16)(uint16_t) = fs->val.conv16;
+       struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf;
+       int i, j, k;
+
+       memset(buf, 0, V7FS_BSIZE);
+
+       for (i = V7FS_MAX_FREEBLOCK - 1, j = listblk + 1, k = 0; i >= 0;
+           i--, j++, k++) {
+               progress(0);
+               if (j == (int32_t)fs->superblock.volume_size)
+               {
+                       VPRINTF(4, "\nlast freeblock #%d\n",
+                           (*val32)(fb->freeblock[i + 1]));
+
+                       memmove(fb->freeblock + 1, fb->freeblock + i + 1, k *
+                           sizeof(v7fs_daddr_t));
+                       fb->freeblock[0] = 0; /* Terminate link; */
+                       fb->nfreeblock = (*val16)(k + 1);
+                       VPRINTF(4, "last freeblock contains #%d\n",
+                           (*val16)(fb->nfreeblock));
+                       fs->io.write(fs->io.cookie, buf, listblk);
+                       return 0;
+               }
+               fb->freeblock[i] = (*val32)(j);
+       }
+       fb->nfreeblock = (*val16)(k);
+
+       if (!fs->io.write(fs->io.cookie, buf, listblk)) {
+               errno = EIO;
+               warn("blk=%ld", (long)listblk);
+               return 0;
+       }
+
+       /* Return next link block */
+       return (*val32)(fb->freeblock[0]);
+}
+
+static int
+make_filesystem(struct v7fs_self *fs, v7fs_daddr_t volume_size,
+    v7fs_daddr_t ilist_size)
+{
+       struct v7fs_superblock *sb;
+       v7fs_daddr_t blk;
+       uint8_t buf[V7FS_BSIZE];
+       int error = 0;
+       int32_t i, j;
+
+       /* Setup ilist. (ilist must be zero filled. becuase of they are free) */
+       VPRINTF(4, "Zero clear ilist.\n");
+       progress(&(struct progress_arg){ .label = "zero ilist", .tick =
+           ilist_size / PROGRESS_BAR_GRANULE });
+       memset(buf, 0, sizeof buf);
+       for (i = V7FS_ILIST_SECTOR; i < (int32_t)ilist_size; i++) {
+               fs->io.write(fs->io.cookie, buf, i);
+               progress(0);
+       }
+#ifndef HAVE_NBTOOL_CONFIG_H
+       progress_done();
+#endif
+       VPRINTF(4, "\n");
+
+       /* Construct superblock */
+       sb = &fs->superblock;
+       sb->volume_size = volume_size;
+       sb->datablock_start_sector = ilist_size + V7FS_ILIST_SECTOR;
+       sb->update_time = time(NULL);
+
+       /* fill free inode cache. */
+       VPRINTF(4, "Setup inode cache.\n");
+       sb->nfreeinode = V7FS_MAX_FREEINODE;
+       for (i = V7FS_MAX_FREEINODE - 1, j = V7FS_ROOT_INODE; i >= 0; i--, j++)
+               sb->freeinode[i] = j;
+       sb->total_freeinode = ilist_size * V7FS_INODE_PER_BLOCK - 1;
+
+       /* fill free block cache. */
+       VPRINTF(4, "Setup free block cache.\n");
+       sb->nfreeblock = V7FS_MAX_FREEBLOCK;
+       for (i = V7FS_MAX_FREEBLOCK - 1, j = sb->datablock_start_sector; i >= 0;
+           i--, j++)
+               sb->freeblock[i] = j;
+
+       sb->total_freeblock = volume_size - sb->datablock_start_sector;
+
+       /* Write superblock. */
+       sb->modified = 1;
+       if ((error = v7fs_superblock_writeback(fs))) {
+               errno = error;
+               warn("Can't write back superblock.");
+               return error;
+       }
+
+       /* Construct freeblock list */
+       VPRINTF(4, "Setup whole freeblock list.\n");
+       progress(&(struct progress_arg){ .label = "freeblock list", .tick =
+           (volume_size - sb->datablock_start_sector) / PROGRESS_BAR_GRANULE});
+       blk = sb->freeblock[0];
+       while ((blk = make_freeblocklist(fs, blk, buf)))
+               continue;
+#ifndef HAVE_NBTOOL_CONFIG_H
+       progress_done();
+#endif
+
+       VPRINTF(4, "done.\n");
+
+       return 0;
+}
+
+int
+v7fs_newfs(const struct v7fs_mount_device *mount, int32_t maxfile)
+{
+       struct v7fs_self *fs;
+       v7fs_daddr_t ilist_size;
+       int error;
+       v7fs_daddr_t volume_size = mount->sectors;
+
+       /* Check and determine ilistblock, datablock size. */
+       if (volume_size > V7FS_DADDR_MAX + 1) {
+               warnx("volume size %d over v7fs limit %d. truncated.",
+                   volume_size, V7FS_DADDR_MAX + 1);
+               volume_size = V7FS_DADDR_MAX + 1;
+       }
+
+       ilist_size = determine_ilist_size(volume_size, maxfile);
+
+       VPRINTF(1, "volume size=%d, ilist size=%d, endian=%d, NAME_MAX=%d\n",
+           volume_size, ilist_size, mount->endian, V7FS_NAME_MAX);
+
+       /* Setup I/O ops. */
+       if ((error = v7fs_io_init(&fs, mount, V7FS_BSIZE))) {
+               errno = error;
+               warn("I/O setup failed.");
+               return error;
+       }
+       fs->endian = mount->endian;
+       v7fs_endian_init(fs);
+
+       if ((error = partition_check(fs))) {
+               return error;
+       }
+
+       /* Construct filesystem. */
+       if ((error = make_filesystem(fs, volume_size, ilist_size))) {
+               return error;
+       }
+
+       /* Setup root. */
+       if ((error = make_root(fs))) {
+               return error;
+       }
+
+       v7fs_io_fini(fs);
+
+       return 0;
+}
diff --git a/sbin/newfs_v7fs/newfs_v7fs.8 b/sbin/newfs_v7fs/newfs_v7fs.8
new file mode 100644 (file)
index 0000000..7dd3669
--- /dev/null
@@ -0,0 +1,130 @@
+.\"    $NetBSD: newfs_v7fs.8,v 1.3 2011/08/10 11:31:49 uch Exp $
+.\"
+.\" Copyright (c) 2011 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by UCHIYAMA Yasushi.
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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) 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.
+.\"
+.\"     @(#)newlfs.8   8.1 (Berkeley) 6/19/93
+.\"
+.Dd April 29, 2011
+.Dt NEWFS_V7FS 8
+.Os
+.Sh NAME
+.Nm newfs_v7fs
+.Nd construct a new 7th Edition(V7) File System
+.Sh SYNOPSIS
+.Nm
+.Op Fl FZ
+.Op Fl B Ar byte-order
+.Op Fl n Ar inodes
+.Op Fl s Ar sectors
+.Op Fl V Ar verbose
+.Ar special
+.Sh DESCRIPTION
+.Nm
+builds a 7th Edition(V7) file system on the specified
+.Ar special .
+If it is a device, the size information will be taken from the disk label and
+before running
+.Nm
+the disk must be labeled using
+.Xr disklabel 8 ;
+the proper fstype is
+.Dq Version 7 .
+Otherwise, the size must be specified on the command line.
+V7 filesystem's block size and sector size are 512 byte.
+Disk address limits are 24 bit.
+.Pp
+The following arguments are supported:
+.Bl -tag -width XBXbyteXorderXX
+.It Fl B Ar byte-order
+Specify the metadata byte order of the file system to be created.
+Valid byte orders are
+.Sq be ,
+.Sq le ,
+and
+.Sq pdp .
+If no byte order is specified, the file system is created in host
+byte order.
+.It Fl F
+Create file system to a regular file (needs the
+.Fl s
+option).
+.It Fl n Ar inodes
+This specifies the number of inodes for the filesystem.
+If the number of inodes exceeds 65536, it is reduced to 65536.
+.It Fl s Ar sectors
+Create file system with specified number of disk sectors.
+.It Fl V Ar verbose
+This controls the amount of information written to stdout:
+.Bl -tag -width 3n -offset indent -compact
+.It 0
+No output.
+.It 1
+Overall size, ilist size, endian and filename length.
+.It 2
+A progress bar.
+.It 3
+.It 4
+More verbose message.
+.El
+The default is 3.
+.It Fl Z
+Fill file with zeroes instead of creating a sparse file.
+.El
+.Sh SEE ALSO
+.Xr disklabel 5 ,
+.Xr disktab 5 ,
+.\" .Xr fs 5 ,
+.Xr disklabel 8 ,
+.Xr diskpart 8
diff --git a/sbin/newfs_v7fs/newfs_v7fs.c b/sbin/newfs_v7fs/newfs_v7fs.c
new file mode 100644 (file)
index 0000000..565b5fd
--- /dev/null
@@ -0,0 +1,240 @@
+/*     $NetBSD: newfs_v7fs.c,v 1.3 2011/08/10 12:13:20 wiz Exp $ */
+
+/*-
+ * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: newfs_v7fs.c,v 1.3 2011/08/10 12:13:20 wiz Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+
+#include <fs/v7fs/v7fs.h>
+#include "v7fs_impl.h"
+#include "progress.h"
+#include "newfs_v7fs.h"
+
+static void usage(void) __dead;
+static bool progress_bar_enable = false;
+int v7fs_newfs_verbose = 3;    /* newfs compatible */
+
+int
+main(int argc, char **argv)
+{
+       const char *device;
+       struct disklabel d;
+       struct partition *p;
+       struct stat st;
+       uint32_t partsize;
+       int Fflag, Zflag;
+       int part;
+       int fd, ch;
+       int endian = _BYTE_ORDER;
+       int32_t maxfile = 0;
+
+       if (argc < 2)
+               usage();
+
+       Fflag = Zflag = partsize = 0;
+       while ((ch = getopt(argc, argv, "Fs:Zs:n:B:V:")) != -1) {
+               switch (ch) {
+               case 'V':
+                       v7fs_newfs_verbose = atoi(optarg);
+                       break;
+               case 'F':
+                       Fflag = 1;
+                       break;
+               case 's':
+                       partsize = atoi(optarg);
+                       break;
+               case 'n':
+                       maxfile = atoi(optarg);
+                       break;
+               case 'Z':
+                       Zflag = 1;
+                       break;
+               case 'B':
+                       switch (optarg[0]) {
+                       case 'l':
+                               endian = _LITTLE_ENDIAN;
+                               break;
+                       case 'b':
+                               endian = _BIG_ENDIAN;
+                               break;
+                       case 'p':
+                               endian = _PDP_ENDIAN;
+                               break;
+                       }
+                       break;
+               default:
+                       usage();
+                       /*NOTREACHED*/
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (argc != 1)
+               usage();
+       device = argv[0];
+
+       progress_bar_enable = v7fs_newfs_verbose > 1;
+
+
+       if (progress_bar_enable) {
+               progress_switch(progress_bar_enable);
+               progress_init();
+               progress(&(struct progress_arg){ .cdev = device });
+       }
+
+       if (!Fflag) {
+               if ((fd = open(device, O_RDWR)) == -1) {
+                       err(EXIT_FAILURE, "%s", device);
+               }
+               if (fstat(fd, &st) != 0) {
+                       goto err_exit;
+               }
+               if (!S_ISCHR(st.st_mode)) {
+                       warnx("not a raw device.\n");
+               }
+
+               part = DISKPART(st.st_rdev);
+
+               if (ioctl(fd, DIOCGDINFO, &d) == -1) {
+                       goto err_exit;
+               }
+               p = &d.d_partitions[part];
+               if (v7fs_newfs_verbose) {
+                       printf("partition=%d size=%d offset=%d fstype=%d"
+                           " secsize=%d\n", part, p->p_size, p->p_offset,
+                           p->p_fstype, d.d_secsize);
+               }
+               if (p->p_fstype != FS_V7) {
+                       warnx("not a Version 7 partition.");
+                       goto err_exit;
+               }
+               partsize = p->p_size;
+       } else {
+               off_t filesize;
+               uint8_t zbuf[8192] = {0, };
+
+               if (partsize == 0) {
+                       errx(EXIT_FAILURE, "-F requires -s");
+               }
+
+               filesize = partsize << V7FS_BSHIFT;
+
+               fd = open(device, O_RDWR|O_CREAT|O_TRUNC, 0666);
+               if (fd == -1) {
+                       err(EXIT_FAILURE, "%s", device);
+               }
+
+               if (Zflag) {
+                       while (filesize > 0) {
+                               size_t writenow = MIN(filesize,
+                                   (off_t)sizeof(zbuf));
+
+                               if ((size_t)write(fd, zbuf, writenow) !=
+                                   writenow) {
+                                       err(EXIT_FAILURE, NULL);
+                               }
+                               filesize -= writenow;
+                       }
+               } else {
+                       if (lseek(fd, filesize - 1, SEEK_SET) == -1) {
+                               goto err_exit;
+                       }
+                       if (write(fd, zbuf, 1) != 1) {
+                               goto err_exit;
+                       }
+                       if (lseek(fd, 0, SEEK_SET) == -1) {
+                               goto err_exit;
+                       }
+               }
+       }
+
+       if (v7fs_newfs(&(struct v7fs_mount_device)
+               { .device.fd = fd, .endian = endian, .sectors = partsize },
+               maxfile) != 0)
+               goto err_exit;
+
+       close(fd);
+
+       return EXIT_SUCCESS;
+ err_exit:
+       close(fd);
+       err(EXIT_FAILURE, NULL);
+}
+
+void
+progress(const struct progress_arg *p)
+{
+       static struct progress_arg Progress;
+       static char cdev[32];
+       static char label[32];
+
+       if (!progress_bar_enable)
+               return;
+
+       if (p) {
+               Progress = *p;
+               if (p->cdev)
+                       strcpy(cdev, p->cdev);
+               if (p->label)
+                       strcpy(label, p->label);
+       }
+
+       if (!Progress.tick)
+               return;
+       if (++Progress.cnt > Progress.tick) {
+               Progress.cnt = 0;
+               Progress.total++;
+               progress_bar(cdev, label, Progress.total, PROGRESS_BAR_GRANULE);
+       }
+}
+
+static void
+usage(void)
+{
+
+       (void)fprintf(stderr, "usage: \n%s [-FZ] [-B byte-order]"
+           " [-n inodes] [-s sectors] [-V verbose] special\n", getprogname());
+
+       exit(EXIT_FAILURE);
+}
diff --git a/sbin/newfs_v7fs/newfs_v7fs.h b/sbin/newfs_v7fs/newfs_v7fs.h
new file mode 100644 (file)
index 0000000..52ddcea
--- /dev/null
@@ -0,0 +1,48 @@
+/*     $NetBSD: newfs_v7fs.h,v 1.2 2011/08/10 11:31:49 uch Exp $       */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _SBIN_NEWFS_V7FS_NEWFS_V7FS_H_
+#define        _SBIN_NEWFS_V7FS_NEWFS_V7FS_H_
+
+#define        PROGRESS_BAR_GRANULE    100
+struct progress_arg {
+       const char *cdev;
+       const char *label;
+       off_t tick;
+       off_t cnt;
+       off_t total;
+};
+__BEGIN_DECLS
+void progress(const struct progress_arg *);
+int v7fs_newfs(const struct v7fs_mount_device *, int32_t);
+extern int v7fs_newfs_verbose;
+__END_DECLS
+#endif /* !_SBIN_NEWFS_V7FS_NEWFS_V7FS_H_ */
index 42a39fb7fa1700420e7d2c0f592fb7ec199c62a7..1cb94a587a7d843e82f4c8148b5961f72d0f0280 100644 (file)
@@ -1,6 +1,6 @@
 #      $NetBSD: Makefile,v 1.20 2011/06/27 11:52:24 uch Exp $
 
 SUBDIR=         cd9660 msdosfs \
-       puffs
+       puffs udf v7fs
 
 .include <bsd.kinc.mk>
diff --git a/sys/fs/udf/Makefile b/sys/fs/udf/Makefile
new file mode 100644 (file)
index 0000000..02c65a1
--- /dev/null
@@ -0,0 +1,7 @@
+#      $NetBSD: Makefile,v 1.1 2006/02/02 15:19:15 reinoud Exp $
+
+INCSDIR= /usr/include/fs/udf
+
+INCS=  ecma167-udf.h udf_mount.h
+
+.include <bsd.kinc.mk>
diff --git a/sys/fs/udf/ecma167-udf.h b/sys/fs/udf/ecma167-udf.h
new file mode 100644 (file)
index 0000000..c52b99d
--- /dev/null
@@ -0,0 +1,835 @@
+/* $NetBSD: ecma167-udf.h,v 1.14 2011/07/07 17:45:38 reinoud Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2008, 2009
+ *     Reinoud Zandijk * <reinoud@NetBSD.org>
+ * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * 
+ * Extended and adapted for UDFv2.50+ bij Reinoud Zandijk based on the
+ * original by Scott Long.
+ * 
+ * 20030508 Made some small typo and explanatory comments
+ * 20030510 Added UDF 2.01 structures
+ * 20030519 Added/correct comments on multi-partitioned logical volume space
+ * 20050616 Added pseudo overwrite
+ * 20050624 Added the missing extended attribute types and `magic values'.
+ * 20051106 Reworked some implementation use parts
+ *
+ */
+
+
+#ifndef _FS_UDF_ECMA167_UDF_H_
+#define _FS_UDF_ECMA167_UDF_H_
+
+
+/*
+ * in case of an older gcc versions, define the __packed as explicit
+ * attribute
+ */
+
+/*
+ * You may specify the `aligned' and `transparent_union' attributes either in
+ * a `typedef' declaration or just past the closing curly brace of a complete
+ * enum, struct or union type _definition_ and the `packed' attribute only
+ * past the closing brace of a definition.  You may also specify attributes
+ * between the enum, struct or union tag and the name of the type rather than
+ * after the closing brace.
+*/
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+
+/* ecma167-udf.h */
+
+/* Volume recognition sequence ECMA 167 rev. 3 16.1 */
+struct vrs_desc {
+       uint8_t                 struct_type;
+       uint8_t                 identifier[5];
+       uint8_t                 version;
+       uint8_t                 data[2041];
+} __packed;
+
+
+#define VRS_NSR02              "NSR02"
+#define VRS_NSR03              "NSR03"
+#define VRS_BEA01              "BEA01"
+#define VRS_TEA01              "TEA01"
+#define VRS_CD001              "CD001"
+#define VRS_CDW02              "CDW02"
+
+
+/* Structure/definitions/constants a la ECMA 167 rev. 3 */
+
+
+#define MAX_TAGID_VOLUMES 9
+/* Tag identifiers */
+enum {
+       TAGID_SPARING_TABLE =     0,
+       TAGID_PRI_VOL =           1,
+       TAGID_ANCHOR =            2,
+       TAGID_VOL =               3,
+       TAGID_IMP_VOL =           4,
+       TAGID_PARTITION =         5,
+       TAGID_LOGVOL =            6,
+       TAGID_UNALLOC_SPACE =     7,
+       TAGID_TERM =              8,
+       TAGID_LOGVOL_INTEGRITY=   9,
+       TAGID_FSD =             256,
+       TAGID_FID =             257,
+       TAGID_ALLOCEXTENT =     258,
+       TAGID_INDIRECTENTRY =   259,
+       TAGID_ICB_TERM =        260,
+       TAGID_FENTRY =          261,
+       TAGID_EXTATTR_HDR =     262,
+       TAGID_UNALL_SP_ENTRY =  263,
+       TAGID_SPACE_BITMAP =    264,
+       TAGID_PART_INTEGRITY =  265,
+       TAGID_EXTFENTRY =       266,
+       TAGID_MAX =             266
+};
+
+
+enum {
+       UDF_DOMAIN_FLAG_HARD_WRITE_PROTECT = 1,
+       UDF_DOMAIN_FLAG_SOFT_WRITE_PROTECT = 2
+};
+
+
+enum {
+       UDF_ACCESSTYPE_NOT_SPECIFIED   = 0,     /* unknown                              */
+       UDF_ACCESSTYPE_PSEUDO_OVERWITE = 0,     /* pseudo overwritable, e.g. BD-R's LOW */
+       UDF_ACCESSTYPE_READ_ONLY       = 1,     /* really only readable                 */
+       UDF_ACCESSTYPE_WRITE_ONCE      = 2,     /* write once and you're done           */
+       UDF_ACCESSTYPE_REWRITEABLE     = 3,     /* may need extra work to rewrite       */
+       UDF_ACCESSTYPE_OVERWRITABLE    = 4      /* no limits on rewriting; e.g. harddisc*/
+};
+
+
+/* Descriptor tag [3/7.2] */
+struct desc_tag {
+       uint16_t        id;
+       uint16_t        descriptor_ver;
+       uint8_t         cksum;
+       uint8_t         reserved;
+       uint16_t        serial_num;
+       uint16_t        desc_crc;
+       uint16_t        desc_crc_len;
+       uint32_t        tag_loc;
+} __packed;
+#define UDF_DESC_TAG_LENGTH 16
+
+
+/* Recorded Address [4/7.1] */
+struct lb_addr {                       /* within partition space */
+       uint32_t        lb_num;
+       uint16_t        part_num;
+} __packed;
+
+
+/* Extent Descriptor [3/7.1] */
+struct extent_ad {
+       uint32_t        len;
+       uint32_t        loc;
+} __packed;
+
+
+/* Short Allocation Descriptor [4/14.14.1] */
+struct short_ad {
+       uint32_t        len;
+       uint32_t        lb_num;
+} __packed;
+
+
+/* Long Allocation Descriptor [4/14.14.2] */
+struct UDF_ADImp_use {
+       uint16_t        flags;
+       uint32_t        unique_id;
+} __packed;
+#define UDF_ADIMP_FLAGS_EXTENT_ERASED 1
+
+
+struct long_ad {
+       uint32_t        len;
+       struct lb_addr  loc;                    /* within a logical volume mapped partition space !! */
+       union {
+               uint8_t bytes[6];
+               struct UDF_ADImp_use im_used;
+       } impl;
+} __packed;
+#define longad_uniqueid impl.im_used.unique_id
+
+
+/* Extended Allocation Descriptor [4/14.14.3] ; identifies an extent of allocation descriptors ; also in UDF ? */
+struct ext_ad {
+       uint32_t        ex_len;
+       uint32_t        rec_len;
+       uint32_t        inf_len;
+       struct lb_addr  ex_loc;
+       uint8_t         reserved[2];
+} __packed;
+
+
+/* ICB : Information Control Block; positioning */
+union icb {
+       struct short_ad s_ad;
+       struct long_ad  l_ad;
+       struct ext_ad   e_ad;
+};
+
+
+/* short/long/ext extent have flags encoded in length */
+#define UDF_EXT_ALLOCATED              (0<<30)
+#define UDF_EXT_FREED                  (1<<30)
+#define UDF_EXT_ALLOCATED_BUT_NOT_USED (1<<30)
+#define UDF_EXT_FREE                   (2<<30)
+#define UDF_EXT_REDIRECT               (3<<30)
+#define UDF_EXT_FLAGS(len) ((len) & (3<<30))
+#define UDF_EXT_LEN(len)   ((len) & ((1<<30)-1))
+#define UDF_EXT_MAXLEN     ((1<<30)-1)
+
+
+/* Character set spec [1/7.2.1] */
+struct charspec {
+       uint8_t         type;
+       uint8_t         inf[63];
+} __packed;
+
+
+struct pathcomp {
+       uint8_t         type;
+       uint8_t         l_ci;
+       uint16_t        comp_filever;
+       uint8_t         ident[256];
+} __packed;
+#define        UDF_PATH_COMP_SIZE 4
+#define UDF_PATH_COMP_RESERVED         0
+#define UDF_PATH_COMP_ROOT             1
+#define UDF_PATH_COMP_MOUNTROOT                2
+#define UDF_PATH_COMP_PARENTDIR                3
+#define UDF_PATH_COMP_CURDIR           4
+#define UDF_PATH_COMP_NAME             5
+
+
+/* Timestamp [1/7.3] */
+struct timestamp {
+       uint16_t        type_tz;
+       uint16_t        year;
+       uint8_t         month;
+       uint8_t         day;
+       uint8_t         hour;
+       uint8_t         minute;
+       uint8_t         second;
+       uint8_t         centisec;
+       uint8_t         hund_usec;
+       uint8_t         usec;
+} __packed;
+#define UDF_TIMESTAMP_SIZE 12
+
+
+/* Entity Identifier [1/7.4] */
+#define        UDF_REGID_ID_SIZE       23
+struct regid {
+       uint8_t         flags;
+       uint8_t         id[UDF_REGID_ID_SIZE];
+       uint8_t         id_suffix[8];
+} __packed;
+
+
+/* ICB Tag [4/14.6] */
+struct icb_tag {
+       uint32_t        prev_num_dirs;
+       uint16_t        strat_type;
+       uint8_t         strat_param[2];
+       uint16_t        max_num_entries;
+       uint8_t         reserved;
+       uint8_t         file_type;
+       struct lb_addr  parent_icb;
+       uint16_t        flags;
+} __packed;
+#define UDF_ICB_TAG_FLAGS_ALLOC_MASK   0x03
+#define UDF_ICB_SHORT_ALLOC            0x00
+#define UDF_ICB_LONG_ALLOC             0x01
+#define UDF_ICB_EXT_ALLOC              0x02
+#define UDF_ICB_INTERN_ALLOC           0x03
+
+#define UDF_ICB_TAG_FLAGS_DIRORDERED   (1<< 3)
+#define UDF_ICB_TAG_FLAGS_NONRELOC     (1<< 4)
+#define UDF_ICB_TAG_FLAGS_CONTIGUES    (1<< 9)
+#define UDF_ICB_TAG_FLAGS_MULTIPLEVERS (1<<12)
+
+#define        UDF_ICB_TAG_FLAGS_SETUID        (1<< 6)
+#define        UDF_ICB_TAG_FLAGS_SETGID        (1<< 7)
+#define        UDF_ICB_TAG_FLAGS_STICKY        (1<< 8)
+
+#define UDF_ICB_FILETYPE_UNKNOWN         0
+#define UDF_ICB_FILETYPE_UNALLOCSPACE    1
+#define UDF_ICB_FILETYPE_PARTINTEGRITY    2
+#define UDF_ICB_FILETYPE_INDIRECTENTRY   3
+#define UDF_ICB_FILETYPE_DIRECTORY       4
+#define UDF_ICB_FILETYPE_RANDOMACCESS    5
+#define UDF_ICB_FILETYPE_BLOCKDEVICE     6
+#define UDF_ICB_FILETYPE_CHARDEVICE      7
+#define UDF_ICB_FILETYPE_EXTATTRREC      8
+#define UDF_ICB_FILETYPE_FIFO            9
+#define UDF_ICB_FILETYPE_SOCKET                 10
+#define UDF_ICB_FILETYPE_TERM           11
+#define UDF_ICB_FILETYPE_SYMLINK        12
+#define UDF_ICB_FILETYPE_STREAMDIR      13
+#define UDF_ICB_FILETYPE_VAT           248
+#define UDF_ICB_FILETYPE_REALTIME      249
+#define UDF_ICB_FILETYPE_META_MAIN     250
+#define UDF_ICB_FILETYPE_META_MIRROR   251
+#define UDF_ICB_FILETYPE_META_BITMAP   252
+
+
+/* Anchor Volume Descriptor Pointer [3/10.2] */
+struct anchor_vdp {
+       struct desc_tag         tag;
+       struct extent_ad        main_vds_ex;            /* to main volume descriptor set      ; 16 sectors min */
+       struct extent_ad        reserve_vds_ex;         /* copy of main volume descriptor set ; 16 sectors min */
+} __packed;
+
+
+/* Volume Descriptor Pointer [3/10.3] */
+struct vol_desc_ptr {
+       struct desc_tag         tag;                    /* use for extending the volume descriptor space */
+       uint32_t                vds_number;
+       struct extent_ad        next_vds_ex;            /* points to the next block for volume descriptor space */
+} __packed;
+
+
+/* Primary Volume Descriptor [3/10.1] */
+struct pri_vol_desc {
+       struct desc_tag         tag;
+       uint32_t                seq_num;                /* MAX prevail */
+       uint32_t                pvd_num;                /* assigned by author; 0 is special as in it may only occur once */
+       char                    vol_id[32];             /* KEY ; main identifier of this disc */
+       uint16_t                vds_num;                /* volume descriptor number; i.e. what volume number is it */
+       uint16_t                max_vol_seq;            /* maximum volume descriptor number known */
+       uint16_t                ichg_lvl;
+       uint16_t                max_ichg_lvl;
+       uint32_t                charset_list;
+       uint32_t                max_charset_list;
+       char                    volset_id[128];         /* KEY ; if part of a multi-disc set or a band of volumes */
+       struct charspec         desc_charset;           /* KEY according to ECMA 167 */
+       struct charspec         explanatory_charset;
+       struct extent_ad        vol_abstract;
+       struct extent_ad        vol_copyright;
+       struct regid            app_id;
+       struct timestamp        time;
+       struct regid            imp_id;
+       uint8_t                 imp_use[64];
+       uint32_t                prev_vds_loc;           /* location of predecessor _lov ? */
+       uint16_t                flags;                  /* bit 0 : if set indicates volume set name is meaningful */
+       uint8_t                 reserved[22];
+} __packed;
+
+
+/* UDF specific implementation use part of the implementation use volume descriptor */
+struct udf_lv_info {
+       struct charspec         lvi_charset;
+       char                    logvol_id[128];
+
+       char                    lvinfo1[36];
+       char                    lvinfo2[36];
+       char                    lvinfo3[36];
+
+       struct regid            impl_id;
+       uint8_t                 impl_use[128];
+} __packed;
+
+
+/* Implementation use Volume Descriptor */
+struct impvol_desc {
+       struct desc_tag         tag;
+       uint32_t                seq_num;
+       struct regid            impl_id;
+       union {
+               struct udf_lv_info      lv_info;
+               char                    impl_use[460];
+       } _impl_use;
+} __packed;
+
+
+/* Logical Volume Descriptor [3/10.6] */
+struct logvol_desc {
+       struct desc_tag         tag;
+       uint32_t                seq_num;                /* MAX prevail */
+       struct charspec         desc_charset;           /* KEY */
+       char                    logvol_id[128];         /* KEY */
+       uint32_t                lb_size;
+       struct regid            domain_id;
+       union {
+               struct long_ad  fsd_loc;                /* to fileset descriptor SEQUENCE */
+               uint8_t         logvol_content_use[16];
+       } _lvd_use;
+       uint32_t                mt_l;                   /* Partition map length */
+       uint32_t                n_pm;                   /* Number of partition maps */
+       struct regid            imp_id;
+       uint8_t                 imp_use[128];
+       struct extent_ad        integrity_seq_loc;
+       uint8_t                 maps[1];
+} __packed;
+#define lv_fsd_loc _lvd_use.fsd_loc
+
+#define UDF_INTEGRITY_OPEN     0
+#define UDF_INTEGRITY_CLOSED   1
+
+
+#define        UDF_PMAP_SIZE   64
+
+/* Type 1 Partition Map [3/10.7.2] */
+struct part_map_1 {
+       uint8_t                 type;
+       uint8_t                 len;
+       uint16_t                vol_seq_num;
+       uint16_t                part_num;
+} __packed;
+
+
+/* Type 2 Partition Map [3/10.7.3] */
+struct part_map_2 {
+       uint8_t                 type;
+       uint8_t                 len;
+       uint8_t                 reserved[2];
+       struct regid            part_id;
+       uint16_t                vol_seq_num;
+       uint16_t                part_num;
+       uint8_t                 reserved2[24];
+} __packed;
+
+
+/* Virtual Partition Map [UDF 2.01/2.2.8] */
+struct part_map_virt {
+       uint8_t                 type;
+       uint8_t                 len;
+       uint8_t                 reserved[2];
+       struct regid            id;
+       uint16_t                vol_seq_num;
+       uint16_t                part_num;
+       uint8_t                 reserved1[24];
+} __packed;
+
+
+/* Sparable Partition Map [UDF 2.01/2.2.9] */
+struct part_map_spare {
+       uint8_t                 type;
+       uint8_t                 len;
+       uint8_t                 reserved[2];
+       struct regid            id;
+       uint16_t                vol_seq_num;
+       uint16_t                part_num;
+       uint16_t                packet_len;
+       uint8_t                 n_st;           /* Number of redundant sparing tables range 1-4 */
+       uint8_t                 reserved1;
+       uint32_t                st_size;        /* size of EACH sparing table  */
+       uint32_t                st_loc[1];      /* locations of sparing tables */
+} __packed;
+
+
+/* Metadata Partition Map [UDF 2.50/2.2.10] */
+struct part_map_meta {
+       uint8_t                 type;
+       uint8_t                 len;
+       uint8_t                 reserved[2];
+       struct regid            id;
+       uint16_t                vol_seq_num;
+       uint16_t                part_num;
+       uint32_t                meta_file_lbn;          /* logical block number for file entry within part_num */
+       uint32_t                meta_mirror_file_lbn;
+       uint32_t                meta_bitmap_file_lbn;
+       uint32_t                alloc_unit_size;        /* allocation unit size in blocks */
+       uint16_t                alignment_unit_size;    /* alignment necessary in blocks  */
+       uint8_t                 flags;
+       uint8_t                 reserved1[5];
+} __packed;
+#define METADATA_DUPLICATED    1
+
+
+union udf_pmap {
+       uint8_t                 data[UDF_PMAP_SIZE];
+       struct part_map_1       pm1;
+       struct part_map_2       pm2;
+       struct part_map_virt    pmv;
+       struct part_map_spare   pms;
+       struct part_map_meta    pmm;
+};
+
+
+/* Sparing Map Entry [UDF 2.01/2.2.11] */
+struct spare_map_entry {
+       uint32_t                org;                    /* partition relative address  */
+       uint32_t                map;                    /* absolute disc address (!) can be in partition, but doesn't have to be */
+} __packed;
+
+
+/* Sparing Table [UDF 2.01/2.2.11] */
+struct udf_sparing_table {
+       struct desc_tag         tag;
+       struct regid            id;
+       uint16_t                rt_l;                   /* Relocation Table len */
+       uint8_t                 reserved[2];
+       uint32_t                seq_num;
+       struct spare_map_entry  entries[1];
+} __packed;
+
+
+#define UDF_NO_PREV_VAT                0xffffffff
+/* UDF 1.50 VAT suffix [UDF 2.2.10 (UDF 1.50 spec)] */
+struct udf_oldvat_tail {
+       struct regid            id;                     /* "*UDF Virtual Alloc Tbl" */
+       uint32_t                prev_vat;
+} __packed;
+
+
+/* VAT table [UDF 2.0.1/2.2.10] */
+struct udf_vat {
+       uint16_t                header_len;
+       uint16_t                impl_use_len;
+       char                    logvol_id[128];         /* newer version of the LVD one */
+       uint32_t                prev_vat;
+       uint32_t                num_files;
+       uint32_t                num_directories;
+       uint16_t                min_udf_readver;
+       uint16_t                min_udf_writever;
+       uint16_t                max_udf_writever;
+       uint16_t                reserved;
+       uint8_t                 data[1];                /* impl.use followed by VAT entries (uint32_t) */
+} __packed;
+
+
+/* Space bitmap descriptor as found in the partition header descriptor */
+struct space_bitmap_desc {
+       struct desc_tag         tag;                    /* TagId 264                    */
+       uint32_t                num_bits;               /* number of bits               */
+       uint32_t                num_bytes;              /* bytes that contain it        */
+       uint8_t                 data[1];
+} __packed;
+
+
+/* Unalloc space entry as found in the partition header descriptor */
+struct space_entry_desc {
+       struct desc_tag         tag;                    /* TagId 263                    */
+       struct icb_tag          icbtag;                 /* type 1                       */
+       uint32_t                l_ad;                   /* in bytes                     */
+       uint8_t                 entry[1];
+} __packed;
+
+
+/* Partition header descriptor; in the contents_use of part_desc */
+struct part_hdr_desc {
+       struct short_ad         unalloc_space_table;
+       struct short_ad         unalloc_space_bitmap;
+       struct short_ad         part_integrity_table;   /* has to be ZERO for UDF */
+       struct short_ad         freed_space_table;
+       struct short_ad         freed_space_bitmap;
+       uint8_t                 reserved[88];
+} __packed;
+
+
+/* Partition Descriptor [3/10.5] */
+struct part_desc {
+       struct desc_tag         tag;
+       uint32_t                seq_num;                /* MAX prevailing */
+       uint16_t                flags;                  /* bit 0 : if set the space is allocated */
+       uint16_t                part_num;               /* KEY */
+       struct regid            contents;
+       union {
+               struct part_hdr_desc    part_hdr;
+               uint8_t                 contents_use[128];
+       } _impl_use;
+       uint32_t                access_type;            /* R/W, WORM etc. */
+       uint32_t                start_loc;              /* start of partition with given length */
+       uint32_t                part_len;
+       struct regid            imp_id;
+       uint8_t                 imp_use[128];
+       uint8_t                 reserved[156];
+} __packed;
+#define pd_part_hdr _impl_use.part_hdr
+#define UDF_PART_FLAG_ALLOCATED                1
+
+
+/* Unallocated Space Descriptor (UDF 2.01/2.2.5) */
+struct unalloc_sp_desc {
+       struct desc_tag         tag;
+       uint32_t                seq_num;        /* MAX prevailing */
+       uint32_t                alloc_desc_num;
+       struct extent_ad        alloc_desc[1];
+} __packed;
+
+
+/* Logical Volume Integrity Descriptor [3/30.10] */
+struct logvolhdr {
+       uint64_t                next_unique_id;
+       /* rest reserved */
+} __packed;
+
+
+struct udf_logvol_info {
+       struct regid            impl_id;
+       uint32_t                num_files;
+       uint32_t                num_directories;
+       uint16_t                min_udf_readver;
+       uint16_t                min_udf_writever;
+       uint16_t                max_udf_writever;
+} __packed;
+
+
+struct logvol_int_desc {
+       struct desc_tag         tag;
+       struct timestamp        time;
+       uint32_t                integrity_type;
+       struct extent_ad        next_extent;
+       union {
+               struct logvolhdr  logvolhdr;
+               int8_t            reserved[32];
+       } _impl_use;
+       uint32_t                num_part;
+       uint32_t                l_iu;
+       uint32_t                tables[1];      /* Freespace table, Sizetable, Implementation use */
+} __packed;
+#define lvint_next_unique_id _impl_use.logvolhdr.next_unique_id
+
+
+/* File Set Descriptor [4/14.1] */
+struct fileset_desc {
+       struct desc_tag         tag;
+       struct timestamp        time;
+       uint16_t                ichg_lvl;
+       uint16_t                max_ichg_lvl;
+       uint32_t                charset_list;
+       uint32_t                max_charset_list;
+       uint32_t                fileset_num;                    /* key! */
+       uint32_t                fileset_desc_num;
+       struct charspec         logvol_id_charset;
+       char                    logvol_id[128];                 /* for recovery                 */
+       struct charspec         fileset_charset;
+       char                    fileset_id[32];                 /* Mountpoint !!                */
+       char                    copyright_file_id[32];
+       char                    abstract_file_id[32];
+       struct long_ad          rootdir_icb;                    /* to rootdir; icb->virtual ?   */
+       struct regid            domain_id;
+       struct long_ad          next_ex;                        /* to the next fileset_desc extent */
+       struct long_ad          streamdir_icb;                  /* streamdir; needed?           */
+       uint8_t                 reserved[32];
+} __packed;
+
+
+/* File Identifier Descriptor [4/14.4] */
+struct fileid_desc {
+       struct desc_tag         tag;
+       uint16_t                file_version_num;
+       uint8_t                 file_char;
+       uint8_t                 l_fi;   /* Length of file identifier area */
+       struct long_ad          icb;
+       uint16_t                l_iu;   /* Length of implementation use area */
+       uint8_t                 data[0];
+} __packed;
+#define        UDF_FID_SIZE    38
+#define        UDF_FILE_CHAR_VIS       (1 << 0) /* Invisible */
+#define        UDF_FILE_CHAR_DIR       (1 << 1) /* Directory */
+#define        UDF_FILE_CHAR_DEL       (1 << 2) /* Deleted */
+#define        UDF_FILE_CHAR_PAR       (1 << 3) /* Parent Directory */
+#define        UDF_FILE_CHAR_META      (1 << 4) /* Stream metadata */
+
+
+/* Extended attributes [4/14.10.1] */
+struct extattrhdr_desc {
+       struct desc_tag         tag;
+       uint32_t                impl_attr_loc;  /* offsets within this descriptor */
+       uint32_t                appl_attr_loc;  /* ditto */
+} __packed;
+#define UDF_IMPL_ATTR_LOC_NOT_PRESENT 0xffffffff
+#define UDF_APPL_ATTR_LOC_NOT_PRESENT 0xffffffff
+
+
+/* Extended attribute entry [4/48.10.2] */
+struct extattr_entry {
+       uint32_t                type;
+       uint8_t                 subtype;
+       uint8_t                 reserved[3];
+       uint32_t                a_l;
+} __packed;
+
+
+/* Extended attribute entry; type 2048 [4/48.10.8] */
+struct impl_extattr_entry {
+       struct extattr_entry    hdr;
+       uint32_t                iu_l;
+       struct regid            imp_id;
+       uint8_t                 data[1];
+} __packed;
+
+
+/* Extended attribute entry; type 65 536 [4/48.10.9] */
+struct appl_extattr_entry {
+       struct extattr_entry    hdr;
+       uint32_t                au_l;
+       struct regid            appl_id;
+       uint8_t                 data[1];
+} __packed;
+
+
+/* File Times attribute entry; type 5 or type 6 [4/48.10.5], [4/48.10.6] */
+struct filetimes_extattr_entry {
+       struct extattr_entry    hdr;
+       uint32_t                d_l;            /* length of times[] data following */
+       uint32_t                existence;      /* bitmask */
+       struct timestamp        times[1];       /* in order of ascending bits */
+} __packed;
+#define UDF_FILETIMES_ATTR_NO  5
+#define UDF_FILETIMES_FILE_CREATION    1
+#define UDF_FILETIMES_FILE_DELETION    4
+#define UDF_FILETIMES_FILE_EFFECTIVE   8
+#define UDF_FILETIMES_FILE_BACKUPED    16
+#define UDF_FILETIMES_ATTR_SIZE(no)    (20 + (no)*sizeof(struct timestamp))
+
+
+/* Device Specification Extended Attribute [4/4.10.7] */
+struct device_extattr_entry {
+       struct extattr_entry    hdr;
+       uint32_t                iu_l;           /* length of implementation use */
+       uint32_t                major;
+       uint32_t                minor;
+       uint8_t                 data[1];        /* UDF: if nonzero length, contain developer ID regid */
+} __packed;
+#define UDF_DEVICESPEC_ATTR_NO 12
+
+
+/* VAT LV extension Extended Attribute [UDF 3.3.4.5.1.3] 1.50 errata */
+struct vatlvext_extattr_entry {
+       uint64_t                unique_id_chk;  /* needs to be copy of ICB's */
+       uint32_t                num_files;
+       uint32_t                num_directories;
+       char                    logvol_id[128]; /* replaces logvol name */
+} __packed;
+
+
+/* File Entry [4/14.9] */
+struct file_entry {
+       struct desc_tag         tag;
+       struct icb_tag          icbtag;
+       uint32_t                uid;
+       uint32_t                gid;
+       uint32_t                perm;
+       uint16_t                link_cnt;
+       uint8_t                 rec_format;
+       uint8_t                 rec_disp_attr;
+       uint32_t                rec_len;
+       uint64_t                inf_len;
+       uint64_t                logblks_rec;
+       struct timestamp        atime;
+       struct timestamp        mtime;
+       struct timestamp        attrtime;
+       uint32_t                ckpoint;
+       struct long_ad          ex_attr_icb;
+       struct regid            imp_id;
+       uint64_t                unique_id;
+       uint32_t                l_ea;   /* Length of extended attribute area */
+       uint32_t                l_ad;   /* Length of allocation descriptors */
+       uint8_t                 data[1];
+} __packed;
+#define        UDF_FENTRY_SIZE 176
+#define        UDF_FENTRY_PERM_USER_MASK       0x07
+#define        UDF_FENTRY_PERM_GRP_MASK        0xE0
+#define        UDF_FENTRY_PERM_OWNER_MASK      0x1C00
+
+
+/* Extended File Entry [4/48.17] */
+struct extfile_entry {
+       struct desc_tag         tag;
+       struct icb_tag          icbtag;
+       uint32_t                uid;
+       uint32_t                gid;
+       uint32_t                perm;
+       uint16_t                link_cnt;
+       uint8_t                 rec_format;
+       uint8_t                 rec_disp_attr;
+       uint32_t                rec_len;
+       uint64_t                inf_len;
+       uint64_t                obj_size;
+       uint64_t                logblks_rec;
+       struct timestamp        atime;
+       struct timestamp        mtime;
+       struct timestamp        ctime;
+       struct timestamp        attrtime;
+       uint32_t                ckpoint;
+       uint32_t                reserved1;
+       struct long_ad          ex_attr_icb;
+       struct long_ad          streamdir_icb;
+       struct regid            imp_id;
+       uint64_t                unique_id;
+       uint32_t                l_ea;   /* Length of extended attribute area */
+       uint32_t                l_ad;   /* Length of allocation descriptors */
+       uint8_t                 data[1];
+} __packed;
+#define        UDF_EXTFENTRY_SIZE      216
+
+
+/* Indirect entry [ecma 48.7] */
+struct indirect_entry {
+       struct desc_tag         tag;
+       struct icb_tag          icbtag;
+       struct long_ad          indirect_icb;
+} __packed;
+
+
+/* Allocation extent descriptor [ecma 48.5] */
+struct alloc_ext_entry {
+       struct desc_tag         tag;
+       uint32_t                prev_entry;
+       uint32_t                l_ad;
+       uint8_t                 data[1];
+} __packed;
+
+
+union dscrptr {
+       struct desc_tag          tag;
+       struct anchor_vdp        avdp;
+       struct vol_desc_ptr      vdp;
+       struct pri_vol_desc      pvd;
+       struct logvol_desc       lvd;
+       struct unalloc_sp_desc   usd;
+       struct logvol_int_desc   lvid;
+       struct impvol_desc       ivd;
+       struct part_desc         pd;
+       struct fileset_desc      fsd;
+       struct fileid_desc       fid;
+       struct file_entry        fe;
+       struct extfile_entry     efe;
+       struct extattrhdr_desc   eahd;
+       struct indirect_entry    inde;
+       struct alloc_ext_entry   aee;
+       struct udf_sparing_table spt;
+       struct space_bitmap_desc sbd;
+       struct space_entry_desc  sed;
+};
+
+
+#endif /* !_FS_UDF_ECMA167_UDF_H_ */
+
diff --git a/sys/fs/udf/files.udf b/sys/fs/udf/files.udf
new file mode 100644 (file)
index 0000000..87fb667
--- /dev/null
@@ -0,0 +1,16 @@
+#      $NetBSD: files.udf,v 1.5 2013/07/10 15:10:56 reinoud Exp $
+
+deffs  UDF
+
+file   fs/udf/udf_osta.c               udf
+file   fs/udf/udf_vfsops.c             udf
+file   fs/udf/udf_vnops.c              udf
+file   fs/udf/udf_subr.c               udf
+file   fs/udf/udf_readwrite.c          udf
+file   fs/udf/udf_strat_bootstrap.c    udf
+file   fs/udf/udf_strat_sequential.c   udf
+file   fs/udf/udf_strat_direct.c       udf
+file   fs/udf/udf_strat_rmw.c          udf
+file   fs/udf/udf_allocation.c         udf
+file   fs/udf/udf_rename.c             udf
+
diff --git a/sys/fs/udf/udf.h b/sys/fs/udf/udf.h
new file mode 100644 (file)
index 0000000..6848203
--- /dev/null
@@ -0,0 +1,426 @@
+/* $NetBSD: udf.h,v 1.46 2013/10/18 19:56:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#ifndef _FS_UDF_UDF_H_
+#define _FS_UDF_UDF_H_
+
+#include <sys/queue.h>
+#include <sys/rbtree.h>
+#include <sys/uio.h>
+#include <sys/mutex.h>
+
+#include "udf_osta.h"
+#include "ecma167-udf.h"
+#include <sys/cdio.h>
+#include <sys/bufq.h>
+#include <sys/disk.h>
+#include <sys/kthread.h>
+#include <miscfs/genfs/genfs_node.h>
+
+/* debug section */
+extern int udf_verbose;
+
+/* undefine UDF_COMPLETE_DELETE to need `purge'; but purge is not implemented */
+#define UDF_COMPLETE_DELETE
+
+/* debug categories */
+#define UDF_DEBUG_VOLUMES      0x0000001
+#define UDF_DEBUG_LOCKING      0x0000002
+#define UDF_DEBUG_NODE         0x0000004
+#define UDF_DEBUG_LOOKUP       0x0000008
+#define UDF_DEBUG_READDIR      0x0000010
+#define UDF_DEBUG_FIDS         0x0000020
+#define UDF_DEBUG_DESCRIPTOR   0x0000040
+#define UDF_DEBUG_TRANSLATE    0x0000080
+#define UDF_DEBUG_STRATEGY     0x0000100
+#define UDF_DEBUG_READ         0x0000200
+#define UDF_DEBUG_WRITE                0x0000400
+#define UDF_DEBUG_CALL         0x0000800
+#define UDF_DEBUG_ATTR         0x0001000
+#define UDF_DEBUG_EXTATTR      0x0002000
+#define UDF_DEBUG_ALLOC                0x0004000
+#define UDF_DEBUG_ADWLK                0x0008000
+#define UDF_DEBUG_DIRHASH      0x0010000
+#define UDF_DEBUG_NOTIMPL      0x0020000
+#define UDF_DEBUG_SHEDULE      0x0040000
+#define UDF_DEBUG_ECCLINE      0x0080000
+#define UDF_DEBUG_SYNC         0x0100000
+#define UDF_DEBUG_PARANOIA     0x0200000
+#define UDF_DEBUG_PARANOIDADWLK        0x0400000
+#define UDF_DEBUG_NODEDUMP     0x0800000
+#define UDF_DEBUG_RESERVE      0x1000000
+
+/* initial value of udf_verbose */
+#define UDF_DEBUGGING          0
+
+#ifdef UDF_DEBUG
+#define DPRINTF(name, arg) { \
+               if (udf_verbose & UDF_DEBUG_##name) {\
+                       printf arg;\
+               };\
+       }
+#define DPRINTFIF(name, cond, arg) { \
+               if (udf_verbose & UDF_DEBUG_##name) { \
+                       if (cond) printf arg;\
+               };\
+       }
+#else
+#define DPRINTF(name, arg) {}
+#define DPRINTFIF(name, cond, arg) {}
+#endif
+
+
+/* constants to identify what kind of identifier we are dealing with */
+#define UDF_REGID_DOMAIN                1
+#define UDF_REGID_UDF                   2
+#define UDF_REGID_IMPLEMENTATION        3
+#define UDF_REGID_APPLICATION           4
+#define UDF_REGID_NAME                 99
+
+
+/* DON'T change these: they identify 13thmonkey's UDF implementation */
+#define APP_NAME               "*NetBSD UDF"
+#define APP_VERSION_MAIN       0
+#define APP_VERSION_SUB                5
+#define IMPL_NAME              "*NetBSD kernel UDF"
+
+
+/* Configuration values */
+#define UDF_INODE_HASHBITS     10
+#define UDF_INODE_HASHSIZE     (1<<UDF_INODE_HASHBITS)
+#define UDF_INODE_HASHMASK     (UDF_INODE_HASHSIZE - 1)
+#define UDF_ECCBUF_HASHBITS    10
+#define UDF_ECCBUF_HASHSIZE    (1<<UDF_ECCBUF_HASHBITS)
+#define UDF_ECCBUF_HASHMASK    (UDF_ECCBUF_HASHSIZE -1)
+
+#define UDF_ECCLINE_MAXFREE    5                       /* picked, needs calculation */
+#define UDF_ECCLINE_MAXBUSY    100                     /* picked, needs calculation */
+
+#define UDF_MAX_MAPPINGS       (MAXPHYS/DEV_BSIZE)     /* 128 */
+#define UDF_VAT_CHUNKSIZE      (64*1024)               /* picked */
+#define UDF_SYMLINKBUFLEN      (64*1024)               /* picked */
+
+#define UDF_DISC_SLACK         (128)                   /* picked, at least 64 kb or 128 */
+#define UDF_ISO_VRS_SIZE       (32*2048)               /* 32 ISO `sectors' */
+
+
+/* structure space */
+#define UDF_ANCHORS            4       /* 256, 512, N-256, N */
+#define UDF_PARTITIONS         4       /* overkill */
+#define UDF_PMAPS              5       /* overkill */
+#define UDF_LVDINT_SEGMENTS    100     /* big overkill */
+#define UDF_LVINT_LOSSAGE      4       /* lose 2 openings */
+#define UDF_MAX_ALLOC_EXTENTS  50      /* overkill */
+
+
+/* constants */
+#define UDF_MAXNAMLEN          255                     /* as per SPEC */
+#define UDF_TRANS_ZERO         ((uint64_t) -1)
+#define UDF_TRANS_UNMAPPED     ((uint64_t) -2)
+#define UDF_TRANS_INTERN       ((uint64_t) -3)
+#define UDF_MAX_SECTOR         ((uint64_t) -10)        /* high water mark */
+
+
+/* RW content hint for allocation and other purposes */
+#define UDF_C_ABSOLUTE          0      /* blob to write at absolute */
+#define UDF_C_PROCESSED                 0      /* not relevant */
+#define UDF_C_USERDATA          1      /* all but userdata is metadata */
+#define UDF_C_DSCR              2      /* update sectornr and CRC */
+#define UDF_C_FLOAT_DSCR        3      /* update sectornr and CRC; sequential */
+#define UDF_C_NODE              4      /* file/dir node, update sectornr and CRC */
+#define UDF_C_FIDS              5      /* update all contained fids */
+#define UDF_C_METADATA_SBM      6      /* space bitmap, update sectornr and CRC */
+#define UDF_C_EXTATTRS          7      /* dunno what to do yet */
+
+/* use unused b_freelistindex for our UDF_C_TYPE */
+#define b_udf_c_type   b_freelistindex
+
+
+/* virtual to physical mapping types */
+#define UDF_VTOP_RAWPART UDF_PMAPS     /* [0..UDF_PMAPS> are normal     */
+
+#define UDF_VTOP_TYPE_RAW            0
+#define UDF_VTOP_TYPE_UNKNOWN        0
+#define UDF_VTOP_TYPE_PHYS           1
+#define UDF_VTOP_TYPE_VIRT           2
+#define UDF_VTOP_TYPE_SPARABLE       3
+#define UDF_VTOP_TYPE_META           4
+
+
+/* allocation strategies */
+#define UDF_ALLOC_INVALID            0
+#define UDF_ALLOC_SEQUENTIAL         1  /* linear on NWA                 */
+#define UDF_ALLOC_VAT                2  /* VAT handling                  */
+#define UDF_ALLOC_SPACEMAP           3  /* spacemaps                     */
+#define UDF_ALLOC_METABITMAP         4  /* metadata bitmap               */
+#define UDF_ALLOC_METASEQUENTIAL     5  /* in chunks seq., nodes not seq */
+#define UDF_ALLOC_RELAXEDSEQUENTIAL  6  /* only nodes not seq.           */
+
+
+/* logical volume open/close actions */
+#define UDF_OPEN_SESSION         0x01  /* if needed writeout VRS + VDS      */
+#define UDF_CLOSE_SESSION        0x02  /* close session after writing VAT   */
+#define UDF_FINALISE_DISC        0x04  /* close session after writing VAT   */
+#define UDF_WRITE_VAT            0x08  /* sequential VAT filesystem         */
+#define UDF_WRITE_LVINT                  0x10  /* write out open lvint              */
+#define UDF_WRITE_PART_BITMAPS   0x20  /* write out partition space bitmaps */
+#define UDF_APPENDONLY_LVINT     0x40  /* no shifting, only appending       */
+#define UDF_WRITE_METAPART_NODES  0x80 /* write out metadata partition nodes*/
+#define UDFLOGVOL_BITS "\20\1OPEN_SESSION\2CLOSE_SESSION\3FINALISE_DISC" \
+                       "\4WRITE_VAT\5WRITE_LVINT\6WRITE_PART_BITMAPS" \
+                       "\7APPENDONLY_LVINT\10WRITE_METAPART_NODES"
+
+/* logical volume error handling actions */
+#define UDF_UPDATE_TRACKINFO     0x01  /* update trackinfo and re-shedule   */
+#define UDF_REMAP_BLOCK                  0x02  /* remap the failing block length    */
+#define UDFONERROR_BITS "\20\1UPDATE_TRACKINFO\2REMAP_BLOCK"
+
+
+/* readdir cookies */
+#define UDF_DIRCOOKIE_DOT 1
+
+
+/* malloc pools */
+MALLOC_DECLARE(M_UDFMNT);
+MALLOC_DECLARE(M_UDFVOLD);
+MALLOC_DECLARE(M_UDFTEMP);
+
+extern struct pool udf_node_pool;
+struct udf_node;
+struct udf_strategy;
+
+
+struct udf_lvintq {
+       uint32_t                start;
+       uint32_t                end;
+       uint32_t                pos;
+       uint32_t                wpos;
+};
+
+
+struct udf_bitmap {
+       uint8_t                 *blob;                  /* allocated         */
+       uint8_t                 *bits;                  /* bits themselves   */
+       uint8_t                 *pages;                 /* dirty pages       */
+       uint32_t                 max_offset;            /* in bits           */
+       uint32_t                 data_pos;              /* position in data  */
+       uint32_t                 metadata_pos;          /* .. in metadata    */
+};
+
+
+struct udf_strat_args {
+       struct udf_mount *ump;
+       struct udf_node  *udf_node;
+       struct long_ad   *icb;
+       union dscrptr    *dscr;
+       struct buf       *nestbuf;
+       kauth_cred_t      cred;
+       int waitfor;
+};
+
+struct udf_strategy {
+       int  (*create_logvol_dscr)  (struct udf_strat_args *args);
+       void (*free_logvol_dscr)    (struct udf_strat_args *args);
+       int  (*read_logvol_dscr)    (struct udf_strat_args *args);
+       int  (*write_logvol_dscr)   (struct udf_strat_args *args);
+       void (*queuebuf)            (struct udf_strat_args *args);
+       void (*discstrat_init)      (struct udf_strat_args *args);
+       void (*discstrat_finish)    (struct udf_strat_args *args);
+};
+
+extern struct udf_strategy udf_strat_bootstrap;
+extern struct udf_strategy udf_strat_sequential;
+extern struct udf_strategy udf_strat_direct;
+extern struct udf_strategy udf_strat_rmw;
+
+
+/* pre cleanup */
+struct udf_mount {
+       struct mount            *vfs_mountp;
+       struct vnode            *devvp; 
+       struct mmc_discinfo      discinfo;
+       struct udf_args          mount_args;
+
+       /* format descriptors */
+       kmutex_t                 logvol_mutex;
+       struct anchor_vdp       *anchors[UDF_ANCHORS];  /* anchors to VDS    */
+       struct pri_vol_desc     *primary_vol;           /* identification    */
+       struct logvol_desc      *logical_vol;           /* main mapping v->p */
+       struct unalloc_sp_desc  *unallocated;           /* free UDF space    */
+       struct impvol_desc      *implementation;        /* likely reduntant  */
+       struct logvol_int_desc  *logvol_integrity;      /* current integrity */
+       struct part_desc        *partitions[UDF_PARTITIONS]; /* partitions   */
+       /* logvol_info is derived; points *into* other structures */
+       struct udf_logvol_info  *logvol_info;           /* integrity descr.  */
+
+       /* fileset and root directories */
+       struct fileset_desc     *fileset_desc;          /* normally one      */
+
+       /* tracing logvol integrity history */
+       struct udf_lvintq        lvint_trace[UDF_LVDINT_SEGMENTS];
+       int                      lvopen;                /* logvol actions    */
+       int                      lvclose;               /* logvol actions    */
+
+       /* logical to physical translations */
+       int                      vtop[UDF_PMAPS+1];     /* vpartnr trans     */
+       int                      vtop_tp[UDF_PMAPS+1];  /* type of trans     */
+
+       /* disc allocation / writing method */
+       kmutex_t                 allocate_mutex;
+       int                      lvreadwrite;           /* error handling    */
+       int                      vtop_alloc[UDF_PMAPS+1]; /* alloc scheme    */
+       int                      data_part;
+       int                      node_part;
+       int                      fids_part;
+
+       /* sequential track info */
+       struct mmc_trackinfo     data_track;
+       struct mmc_trackinfo     metadata_track;
+
+       /* VAT */
+       uint32_t                 first_possible_vat_location;
+       uint32_t                 last_possible_vat_location;
+       uint32_t                 vat_entries;
+       uint32_t                 vat_offset;            /* offset in table   */
+       uint32_t                 vat_last_free_lb;      /* last free lb_num  */
+       uint32_t                 vat_table_len;
+       uint32_t                 vat_table_alloc_len;
+       uint8_t                 *vat_table;
+       uint8_t                 *vat_pages;             /* TODO */
+       struct udf_node         *vat_node;              /* system node       */
+
+       /* space bitmaps for physical partitions */
+       struct space_bitmap_desc*part_unalloc_dscr[UDF_PARTITIONS];
+       struct space_bitmap_desc*part_freed_dscr  [UDF_PARTITIONS];
+       struct udf_bitmap        part_unalloc_bits[UDF_PARTITIONS];
+       struct udf_bitmap        part_freed_bits  [UDF_PARTITIONS];
+
+       /* sparable */
+       uint32_t                 sparable_packet_size;
+       uint32_t                 packet_size;
+       struct udf_sparing_table*sparing_table;
+
+       /* meta */
+       struct udf_node         *metadata_node;         /* system node       */
+       struct udf_node         *metadatamirror_node;   /* system node       */
+       struct udf_node         *metadatabitmap_node;   /* system node       */
+       struct space_bitmap_desc*metadata_unalloc_dscr;
+       struct udf_bitmap        metadata_unalloc_bits;
+       uint32_t                 metadata_alloc_unit_size;
+       uint16_t                 metadata_alignment_unit_size;
+       uint8_t                  metadata_flags;
+
+       /* rb tree for lookup icb to udf_node and sorted list for sync */
+       kmutex_t        ihash_lock;
+       kmutex_t        get_node_lock;
+       struct rb_tree  udf_node_tree;
+
+       /* syncing */
+       int             syncing;                        /* are we syncing?   */
+       kcondvar_t      dirtynodes_cv;                  /* sleeping on sync  */
+
+       /* late allocation */
+       int32_t                  uncommitted_lbs[UDF_PARTITIONS];
+       struct long_ad          *la_node_ad_cpy;                /* issue buf */
+       uint64_t                *la_lmapping, *la_pmapping;     /* issue buf */
+
+       /* lists */
+       STAILQ_HEAD(udfmntpts, udf_mount) all_udf_mntpnts;
+
+       /* device strategy */
+       struct udf_strategy     *strategy;
+       void                    *strategy_private;
+};
+
+/*
+ * UDF node describing a file/directory.
+ *
+ * BUGALERT claim node_mutex before reading/writing to prevent inconsistencies !
+ */
+struct udf_node {
+       struct genfs_node       i_gnode;                /* has to be first   */
+       struct vnode            *vnode;                 /* vnode associated  */
+       struct udf_mount        *ump;
+
+       kmutex_t                 node_mutex;
+       kcondvar_t               node_lock;             /* sleeping lock */
+       char const              *lock_fname;
+       int                      lock_lineno;
+
+       /* rb_node for fast lookup and fast sequential visiting */
+       struct rb_node           rbnode;
+
+       /* one of `fe' or `efe' can be set, not both (UDF file entry dscr.)  */
+       struct file_entry       *fe;
+       struct extfile_entry    *efe;
+       struct alloc_ext_entry  *ext[UDF_MAX_ALLOC_EXTENTS];
+       int                      num_extensions;
+
+       /* location found, recording location & hints */
+       struct long_ad           loc;                   /* FID/hash loc.     */
+       struct long_ad           write_loc;             /* strat 4096 loc    */
+       int                      needs_indirect;        /* has missing indr. */
+       struct long_ad           ext_loc[UDF_MAX_ALLOC_EXTENTS];
+
+       struct dirhash          *dir_hash;
+
+       /* misc */
+       uint32_t                 i_flags;               /* associated flags  */
+       struct lockf            *lockf;                 /* lock list         */
+       uint32_t                 outstanding_bufs;      /* file data         */
+       uint32_t                 outstanding_nodedscr;  /* node dscr         */
+       int32_t                  uncommitted_lbs;       /* in UBC            */
+
+       /* references to associated nodes */
+       struct udf_node         *extattr;
+       struct udf_node         *streamdir;
+       struct udf_node         *my_parent;             /* if extended attr. */
+};
+
+
+/* misc. flags stored in i_flags (XXX needs cleaning up) */
+#define        IN_ACCESS               0x0001  /* Inode access time update request  */
+#define        IN_CHANGE               0x0002  /* Inode change time update request  */
+#define        IN_UPDATE               0x0004  /* Inode was written to; update mtime*/
+#define        IN_MODIFY               0x0008  /* Modification time update request  */
+#define        IN_MODIFIED             0x0010  /* node has been modified */
+#define        IN_ACCESSED             0x0020  /* node has been accessed */
+#define        IN_RENAME               0x0040  /* node is being renamed. XXX ?? */
+#define        IN_DELETED              0x0080  /* node is unlinked, no FID reference*/
+#define        IN_LOCKED               0x0100  /* node is locked by condvar */
+#define        IN_SYNCED               0x0200  /* node is being used by sync */
+#define        IN_CALLBACK_ULK         0x0400  /* node will be unlocked by callback */
+#define        IN_NODE_REBUILD         0x0800  /* node is rebuild */
+
+
+#define IN_FLAGBITS \
+       "\10\1IN_ACCESS\2IN_CHANGE\3IN_UPDATE\4IN_MODIFY\5IN_MODIFIED" \
+       "\6IN_ACCESSED\7IN_RENAME\10IN_DELETED\11IN_LOCKED\12IN_SYNCED" \
+       "\13IN_CALLBACK_ULK\14IN_NODE_REBUILD"
+
+#endif /* !_FS_UDF_UDF_H_ */
diff --git a/sys/fs/udf/udf_allocation.c b/sys/fs/udf/udf_allocation.c
new file mode 100644 (file)
index 0000000..0021169
--- /dev/null
@@ -0,0 +1,3215 @@
+/* $NetBSD: udf_allocation.c,v 1.36 2013/10/30 08:41:38 mrg Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__KERNEL_RCSID(0, "$NetBSD: udf_allocation.c,v 1.36 2013/10/30 08:41:38 mrg Exp $");
+#endif /* not lint */
+
+
+#if defined(_KERNEL_OPT)
+#include "opt_compat_netbsd.h"
+#endif
+
+/* TODO strip */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#include "udf.h"
+#include "udf_subr.h"
+#include "udf_bswap.h"
+
+
+#define VTOI(vnode) ((struct udf_node *) vnode->v_data)
+
+static void udf_record_allocation_in_node(struct udf_mount *ump,
+       struct buf *buf, uint16_t vpart_num, uint64_t *mapping,
+       struct long_ad *node_ad_cpy);
+
+static void udf_collect_free_space_for_vpart(struct udf_mount *ump,
+       uint16_t vpart_num, uint32_t num_lb);
+
+static int udf_ads_merge(uint32_t max_len, uint32_t lb_size, struct long_ad *a1, struct long_ad *a2);
+static void udf_wipe_adslots(struct udf_node *udf_node);
+static void udf_count_alloc_exts(struct udf_node *udf_node);
+
+
+/* --------------------------------------------------------------------- */
+
+#if 0
+#if 1
+static void
+udf_node_dump(struct udf_node *udf_node) {
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       struct icb_tag *icbtag;
+       struct long_ad s_ad;
+       uint64_t inflen;
+       uint32_t icbflags, addr_type;
+       uint32_t len, lb_num;
+       uint32_t flags;
+       int part_num;
+       int lb_size, eof, slot;
+
+       if ((udf_verbose & UDF_DEBUG_NODEDUMP) == 0)
+               return;
+
+       lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
+
+       fe  = udf_node->fe;
+       efe = udf_node->efe;
+       if (fe) {
+               icbtag = &fe->icbtag;
+               inflen = udf_rw64(fe->inf_len);
+       } else {
+               icbtag = &efe->icbtag;
+               inflen = udf_rw64(efe->inf_len);
+       }
+
+       icbflags   = udf_rw16(icbtag->flags);
+       addr_type  = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       printf("udf_node_dump %p :\n", udf_node);
+
+       if (addr_type == UDF_ICB_INTERN_ALLOC) {
+               printf("\tIntern alloc, len = %"PRIu64"\n", inflen);
+               return;
+       }
+
+       printf("\tInflen  = %"PRIu64"\n", inflen);
+       printf("\t\t");
+
+       slot = 0;
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &s_ad, &eof);
+               if (eof)
+                       break;
+               part_num = udf_rw16(s_ad.loc.part_num);
+               lb_num = udf_rw32(s_ad.loc.lb_num);
+               len   = udf_rw32(s_ad.len);
+               flags = UDF_EXT_FLAGS(len);
+               len   = UDF_EXT_LEN(len);
+
+               printf("[");
+               if (part_num >= 0)
+                       printf("part %d, ", part_num);
+               printf("lb_num %d, len %d", lb_num, len);
+               if (flags)
+                       printf(", flags %d", flags>>30);
+               printf("] ");
+
+               if (flags == UDF_EXT_REDIRECT) {
+                       printf("\n\textent END\n\tallocation extent\n\t\t");
+               }
+
+               slot++;
+       }
+       printf("\n\tl_ad END\n\n");
+}
+#else
+#define udf_node_dump(a)
+#endif
+
+
+static void
+udf_assert_allocated(struct udf_mount *ump, uint16_t vpart_num,
+       uint32_t lb_num, uint32_t num_lb)
+{
+       struct udf_bitmap *bitmap;
+       struct part_desc *pdesc;
+       uint32_t ptov;
+       uint32_t bitval;
+       uint8_t *bpos;
+       int bit;
+       int phys_part;
+       int ok;
+
+       DPRINTF(PARANOIA, ("udf_assert_allocated: check virt lbnum %d "
+                         "part %d + %d sect\n", lb_num, vpart_num, num_lb));
+
+       /* get partition backing up this vpart_num */
+       pdesc = ump->partitions[ump->vtop[vpart_num]];
+
+       switch (ump->vtop_tp[vpart_num]) {
+       case UDF_VTOP_TYPE_PHYS :
+       case UDF_VTOP_TYPE_SPARABLE :
+               /* free space to freed or unallocated space bitmap */
+               ptov      = udf_rw32(pdesc->start_loc);
+               phys_part = ump->vtop[vpart_num];
+
+               /* use unallocated bitmap */
+               bitmap = &ump->part_unalloc_bits[phys_part];
+
+               /* if no bitmaps are defined, bail out */
+               if (bitmap->bits == NULL)
+                       break;
+
+               /* check bits */
+               KASSERT(bitmap->bits);
+               ok = 1;
+               bpos = bitmap->bits + lb_num/8;
+               bit  = lb_num % 8;
+               while (num_lb > 0) {
+                       bitval = (1 << bit);
+                       DPRINTF(PARANOIA, ("XXX : check %d, %p, bit %d\n",
+                               lb_num, bpos, bit));
+                       KASSERT(bitmap->bits + lb_num/8 == bpos);
+                       if (*bpos & bitval) {
+                               printf("\tlb_num %d is NOT marked busy\n",
+                                       lb_num);
+                               ok = 0;
+                       }
+                       lb_num++; num_lb--;
+                       bit = (bit + 1) % 8;
+                       if (bit == 0)
+                               bpos++;
+               }
+               if (!ok) {
+                       /* KASSERT(0); */
+               }
+
+               break;
+       case UDF_VTOP_TYPE_VIRT :
+               /* TODO check space */
+               KASSERT(num_lb == 1);
+               break;
+       case UDF_VTOP_TYPE_META :
+               /* TODO check space in the metadata bitmap */
+       default:
+               /* not implemented */
+               break;
+       }
+}
+
+
+static void
+udf_node_sanity_check(struct udf_node *udf_node,
+               uint64_t *cnt_inflen, uint64_t *cnt_logblksrec)
+{
+       union dscrptr *dscr;
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       struct icb_tag *icbtag;
+       struct long_ad  s_ad;
+       uint64_t inflen, logblksrec;
+       uint32_t icbflags, addr_type;
+       uint32_t len, lb_num, l_ea, l_ad, max_l_ad;
+       uint16_t part_num;
+       uint8_t *data_pos;
+       int dscr_size, lb_size, flags, whole_lb;
+       int i, slot, eof;
+
+//     KASSERT(mutex_owned(&udf_node->ump->allocate_mutex));
+
+       if (1)
+               udf_node_dump(udf_node);
+
+       lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
+
+       fe  = udf_node->fe;
+       efe = udf_node->efe;
+       if (fe) {
+               dscr       = (union dscrptr *) fe;
+               icbtag     = &fe->icbtag;
+               inflen     = udf_rw64(fe->inf_len);
+               dscr_size  = sizeof(struct file_entry) -1;
+               logblksrec = udf_rw64(fe->logblks_rec);
+               l_ad       = udf_rw32(fe->l_ad);
+               l_ea       = udf_rw32(fe->l_ea);
+       } else {
+               dscr       = (union dscrptr *) efe;
+               icbtag     = &efe->icbtag;
+               inflen     = udf_rw64(efe->inf_len);
+               dscr_size  = sizeof(struct extfile_entry) -1;
+               logblksrec = udf_rw64(efe->logblks_rec);
+               l_ad       = udf_rw32(efe->l_ad);
+               l_ea       = udf_rw32(efe->l_ea);
+       }
+       data_pos  = (uint8_t *) dscr + dscr_size + l_ea;
+       max_l_ad   = lb_size - dscr_size - l_ea;
+       icbflags   = udf_rw16(icbtag->flags);
+       addr_type  = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       /* check if tail is zero */
+       DPRINTF(PARANOIA, ("Sanity check blank tail\n"));
+       for (i = l_ad; i < max_l_ad; i++) {
+               if (data_pos[i] != 0)
+                       printf( "sanity_check: violation: node byte %d "
+                               "has value %d\n", i, data_pos[i]);
+       }
+
+       /* reset counters */
+       *cnt_inflen     = 0;
+       *cnt_logblksrec = 0;
+
+       if (addr_type == UDF_ICB_INTERN_ALLOC) {
+               KASSERT(l_ad <= max_l_ad);
+               KASSERT(l_ad == inflen);
+               *cnt_inflen = inflen;
+               return;
+       }
+
+       /* start counting */
+       whole_lb = 1;
+       slot = 0;
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &s_ad, &eof);
+               if (eof)
+                       break;
+               KASSERT(whole_lb == 1);
+
+               part_num = udf_rw16(s_ad.loc.part_num);
+               lb_num = udf_rw32(s_ad.loc.lb_num);
+               len   = udf_rw32(s_ad.len);
+               flags = UDF_EXT_FLAGS(len);
+               len   = UDF_EXT_LEN(len);
+
+               if (flags != UDF_EXT_REDIRECT) {
+                       *cnt_inflen += len;
+                       if (flags == UDF_EXT_ALLOCATED) {
+                               *cnt_logblksrec += (len + lb_size -1) / lb_size;
+                       }
+               } else {
+                       KASSERT(len == lb_size);
+               }
+               /* check allocation */
+               if (flags == UDF_EXT_ALLOCATED)
+                       udf_assert_allocated(udf_node->ump, part_num, lb_num,
+                               (len + lb_size - 1) / lb_size);
+
+               /* check whole lb */
+               whole_lb = ((len % lb_size) == 0);
+
+               slot++;
+       }
+       /* rest should be zero (ad_off > l_ad < max_l_ad - adlen) */
+
+       KASSERT(*cnt_inflen == inflen);
+       KASSERT(*cnt_logblksrec == logblksrec);
+
+//     KASSERT(mutex_owned(&udf_node->ump->allocate_mutex));
+}
+#else
+static void
+udf_node_sanity_check(struct udf_node *udf_node,
+               uint64_t *cnt_inflen, uint64_t *cnt_logblksrec) {
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       uint64_t inflen, logblksrec;
+
+       fe  = udf_node->fe;
+       efe = udf_node->efe;
+       if (fe) {
+               inflen = udf_rw64(fe->inf_len);
+               logblksrec = udf_rw64(fe->logblks_rec);
+       } else {
+               inflen = udf_rw64(efe->inf_len);
+               logblksrec = udf_rw64(efe->logblks_rec);
+       }
+       *cnt_logblksrec = logblksrec;
+       *cnt_inflen     = inflen;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+void
+udf_calc_freespace(struct udf_mount *ump, uint64_t *sizeblks, uint64_t *freeblks)
+{
+       struct logvol_int_desc *lvid;
+       uint32_t *pos1, *pos2;
+       int vpart, num_vpart;
+
+       lvid = ump->logvol_integrity;
+       *freeblks = *sizeblks = 0;
+
+       /*
+        * Sequentials media report free space directly (CD/DVD/BD-R), for the
+        * other media we need the logical volume integrity.
+        *
+        * We sum all free space up here regardless of type.
+        */
+
+       KASSERT(lvid);
+       num_vpart = udf_rw32(lvid->num_part);
+
+       if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) {
+               /* use track info directly summing if there are 2 open */
+               /* XXX assumption at most two tracks open */
+               *freeblks = ump->data_track.free_blocks;
+               if (ump->data_track.tracknr != ump->metadata_track.tracknr)
+                       *freeblks += ump->metadata_track.free_blocks;
+               *sizeblks = ump->discinfo.last_possible_lba;
+       } else {
+               /* free and used space for mountpoint based on logvol integrity */
+               for (vpart = 0; vpart < num_vpart; vpart++) {
+                       pos1 = &lvid->tables[0] + vpart;
+                       pos2 = &lvid->tables[0] + num_vpart + vpart;
+                       if (udf_rw32(*pos1) != (uint32_t) -1) {
+                               *freeblks += udf_rw32(*pos1);
+                               *sizeblks += udf_rw32(*pos2);
+                       }
+               }
+       }
+       /* adjust for accounted uncommitted blocks */
+       for (vpart = 0; vpart < num_vpart; vpart++)
+               *freeblks -= ump->uncommitted_lbs[vpart];
+
+       if (*freeblks > UDF_DISC_SLACK) {
+               *freeblks -= UDF_DISC_SLACK;
+       } else {
+               *freeblks = 0;
+       }
+}
+
+
+static void
+udf_calc_vpart_freespace(struct udf_mount *ump, uint16_t vpart_num, uint64_t *freeblks)
+{
+       struct logvol_int_desc *lvid;
+       uint32_t *pos1;
+
+       lvid = ump->logvol_integrity;
+       *freeblks = 0;
+
+       /*
+        * Sequentials media report free space directly (CD/DVD/BD-R), for the
+        * other media we need the logical volume integrity.
+        *
+        * We sum all free space up here regardless of type.
+        */
+
+       KASSERT(lvid);
+       if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) {
+               /* XXX assumption at most two tracks open */
+               if (vpart_num == ump->data_part) {
+                       *freeblks = ump->data_track.free_blocks;
+               } else {
+                       *freeblks = ump->metadata_track.free_blocks;
+               }
+       } else {
+               /* free and used space for mountpoint based on logvol integrity */
+               pos1 = &lvid->tables[0] + vpart_num;
+               if (udf_rw32(*pos1) != (uint32_t) -1)
+                       *freeblks += udf_rw32(*pos1);
+       }
+
+       /* adjust for accounted uncommitted blocks */
+       if (*freeblks > ump->uncommitted_lbs[vpart_num]) {
+               *freeblks -= ump->uncommitted_lbs[vpart_num];
+       } else {
+               *freeblks = 0;
+       }
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_translate_vtop(struct udf_mount *ump, struct long_ad *icb_loc,
+                  uint32_t *lb_numres, uint32_t *extres)
+{
+       struct part_desc       *pdesc;
+       struct spare_map_entry *sme;
+       struct long_ad s_icb_loc;
+       uint64_t foffset, end_foffset;
+       uint32_t lb_size, len;
+       uint32_t lb_num, lb_rel, lb_packet;
+       uint32_t udf_rw32_lbmap, ext_offset;
+       uint16_t vpart;
+       int rel, part, error, eof, slot, flags;
+
+       assert(ump && icb_loc && lb_numres);
+
+       vpart  = udf_rw16(icb_loc->loc.part_num);
+       lb_num = udf_rw32(icb_loc->loc.lb_num);
+       if (vpart > UDF_VTOP_RAWPART)
+               return EINVAL;
+
+translate_again:
+       part = ump->vtop[vpart];
+       pdesc = ump->partitions[part];
+
+       switch (ump->vtop_tp[vpart]) {
+       case UDF_VTOP_TYPE_RAW :
+               /* 1:1 to the end of the device */
+               *lb_numres = lb_num;
+               *extres = INT_MAX;
+               return 0;
+       case UDF_VTOP_TYPE_PHYS :
+               /* transform into its disc logical block */
+               if (lb_num > udf_rw32(pdesc->part_len))
+                       return EINVAL;
+               *lb_numres = lb_num + udf_rw32(pdesc->start_loc);
+
+               /* extent from here to the end of the partition */
+               *extres = udf_rw32(pdesc->part_len) - lb_num;
+               return 0;
+       case UDF_VTOP_TYPE_VIRT :
+               /* only maps one logical block, lookup in VAT */
+               if (lb_num >= ump->vat_entries)         /* XXX > or >= ? */
+                       return EINVAL;
+
+               /* lookup in virtual allocation table file */
+               mutex_enter(&ump->allocate_mutex);
+               error = udf_vat_read(ump->vat_node,
+                               (uint8_t *) &udf_rw32_lbmap, 4,
+                               ump->vat_offset + lb_num * 4);
+               mutex_exit(&ump->allocate_mutex);
+
+               if (error)
+                       return error;
+
+               lb_num = udf_rw32(udf_rw32_lbmap);
+
+               /* transform into its disc logical block */
+               if (lb_num > udf_rw32(pdesc->part_len))
+                       return EINVAL;
+               *lb_numres = lb_num + udf_rw32(pdesc->start_loc);
+
+               /* just one logical block */
+               *extres = 1;
+               return 0;
+       case UDF_VTOP_TYPE_SPARABLE :
+               /* check if the packet containing the lb_num is remapped */
+               lb_packet = lb_num / ump->sparable_packet_size;
+               lb_rel    = lb_num % ump->sparable_packet_size;
+
+               for (rel = 0; rel < udf_rw16(ump->sparing_table->rt_l); rel++) {
+                       sme = &ump->sparing_table->entries[rel];
+                       if (lb_packet == udf_rw32(sme->org)) {
+                               /* NOTE maps to absolute disc logical block! */
+                               *lb_numres = udf_rw32(sme->map) + lb_rel;
+                               *extres    = ump->sparable_packet_size - lb_rel;
+                               return 0;
+                       }
+               }
+
+               /* transform into its disc logical block */
+               if (lb_num > udf_rw32(pdesc->part_len))
+                       return EINVAL;
+               *lb_numres = lb_num + udf_rw32(pdesc->start_loc);
+
+               /* rest of block */
+               *extres = ump->sparable_packet_size - lb_rel;
+               return 0;
+       case UDF_VTOP_TYPE_META :
+               /* we have to look into the file's allocation descriptors */
+
+               /* use metadatafile allocation mutex */
+               lb_size = udf_rw32(ump->logical_vol->lb_size);
+
+               UDF_LOCK_NODE(ump->metadata_node, 0);
+
+               /* get first overlapping extent */
+               foffset = 0;
+               slot    = 0;
+               for (;;) {
+                       udf_get_adslot(ump->metadata_node,
+                               slot, &s_icb_loc, &eof);
+                       DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, "
+                               "len = %d, lb_num = %d, part = %d\n",
+                               slot, eof,
+                               UDF_EXT_FLAGS(udf_rw32(s_icb_loc.len)),
+                               UDF_EXT_LEN(udf_rw32(s_icb_loc.len)),
+                               udf_rw32(s_icb_loc.loc.lb_num),
+                               udf_rw16(s_icb_loc.loc.part_num)));
+                       if (eof) {
+                               DPRINTF(TRANSLATE,
+                                       ("Meta partition translation "
+                                        "failed: can't seek location\n"));
+                               UDF_UNLOCK_NODE(ump->metadata_node, 0);
+                               return EINVAL;
+                       }
+                       len   = udf_rw32(s_icb_loc.len);
+                       flags = UDF_EXT_FLAGS(len);
+                       len   = UDF_EXT_LEN(len);
+
+                       if (flags == UDF_EXT_REDIRECT) {
+                               slot++;
+                               continue;
+                       }
+
+                       end_foffset = foffset + len;
+
+                       if (end_foffset > (uint64_t) lb_num * lb_size)
+                               break;  /* found */
+                       foffset = end_foffset;
+                       slot++;
+               }
+               /* found overlapping slot */
+               ext_offset = lb_num * lb_size - foffset;
+
+               /* process extent offset */
+               lb_num   = udf_rw32(s_icb_loc.loc.lb_num);
+               vpart    = udf_rw16(s_icb_loc.loc.part_num);
+               lb_num  += (ext_offset + lb_size -1) / lb_size;
+               ext_offset = 0;
+
+               UDF_UNLOCK_NODE(ump->metadata_node, 0);
+               if (flags != UDF_EXT_ALLOCATED) {
+                       DPRINTF(TRANSLATE, ("Metadata partition translation "
+                                           "failed: not allocated\n"));
+                       return EINVAL;
+               }
+
+               /*
+                * vpart and lb_num are updated, translate again since we
+                * might be mapped on sparable media
+                */
+               goto translate_again;
+       default:
+               printf("UDF vtop translation scheme %d unimplemented yet\n",
+                       ump->vtop_tp[vpart]);
+       }
+
+       return EINVAL;
+}
+
+
+/* XXX  provisional primitive braindead version */
+/* TODO use ext_res */
+void
+udf_translate_vtop_list(struct udf_mount *ump, uint32_t sectors,
+       uint16_t vpart_num, uint64_t *lmapping, uint64_t *pmapping)
+{
+       struct long_ad loc;
+       uint32_t lb_numres, ext_res;
+       int sector;
+
+       for (sector = 0; sector < sectors; sector++) {
+               memset(&loc, 0, sizeof(struct long_ad));
+               loc.loc.part_num = udf_rw16(vpart_num);
+               loc.loc.lb_num   = udf_rw32(*lmapping);
+               udf_translate_vtop(ump, &loc, &lb_numres, &ext_res);
+               *pmapping = lb_numres;
+               lmapping++; pmapping++;
+       }
+}
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Translate an extent (in logical_blocks) into logical block numbers; used
+ * for read and write operations. DOESNT't check extents.
+ */
+
+int
+udf_translate_file_extent(struct udf_node *udf_node,
+                         uint32_t from, uint32_t num_lb,
+                         uint64_t *map)
+{
+       struct udf_mount *ump;
+       struct icb_tag *icbtag;
+       struct long_ad t_ad, s_ad;
+       uint64_t transsec;
+       uint64_t foffset, end_foffset;
+       uint32_t transsec32;
+       uint32_t lb_size;
+       uint32_t ext_offset;
+       uint32_t lb_num, len;
+       uint32_t overlap, translen;
+       uint16_t vpart_num;
+       int eof, error, flags;
+       int slot, addr_type, icbflags;
+
+       if (!udf_node)
+               return ENOENT;
+
+       KASSERT(num_lb > 0);
+
+       UDF_LOCK_NODE(udf_node, 0);
+
+       /* initialise derivative vars */
+       ump = udf_node->ump;
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+
+       if (udf_node->fe) {
+               icbtag = &udf_node->fe->icbtag;
+       } else {
+               icbtag = &udf_node->efe->icbtag;
+       }
+       icbflags  = udf_rw16(icbtag->flags);
+       addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       /* do the work */
+       if (addr_type == UDF_ICB_INTERN_ALLOC) {
+               *map = UDF_TRANS_INTERN;
+               UDF_UNLOCK_NODE(udf_node, 0);
+               return 0;
+       }
+
+       /* find first overlapping extent */
+       foffset = 0;
+       slot    = 0;
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &s_ad, &eof);
+               DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, len = %d, "
+                       "lb_num = %d, part = %d\n", slot, eof,
+                       UDF_EXT_FLAGS(udf_rw32(s_ad.len)),
+                       UDF_EXT_LEN(udf_rw32(s_ad.len)),
+                       udf_rw32(s_ad.loc.lb_num),
+                       udf_rw16(s_ad.loc.part_num)));
+               if (eof) {
+                       DPRINTF(TRANSLATE,
+                               ("Translate file extent "
+                                "failed: can't seek location\n"));
+                       UDF_UNLOCK_NODE(udf_node, 0);
+                       return EINVAL;
+               }
+               len    = udf_rw32(s_ad.len);
+               flags  = UDF_EXT_FLAGS(len);
+               len    = UDF_EXT_LEN(len);
+               lb_num = udf_rw32(s_ad.loc.lb_num);
+
+               if (flags == UDF_EXT_REDIRECT) {
+                       slot++;
+                       continue;
+               }
+
+               end_foffset = foffset + len;
+
+               if (end_foffset > (uint64_t) from * lb_size)
+                       break;  /* found */
+               foffset = end_foffset;
+               slot++;
+       }
+       /* found overlapping slot */
+       ext_offset = (uint64_t) from * lb_size - foffset;
+
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &s_ad, &eof);
+               DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, len = %d, "
+                       "lb_num = %d, part = %d\n", slot, eof,
+                       UDF_EXT_FLAGS(udf_rw32(s_ad.len)),
+                       UDF_EXT_LEN(udf_rw32(s_ad.len)),
+                       udf_rw32(s_ad.loc.lb_num),
+                       udf_rw16(s_ad.loc.part_num)));
+               if (eof) {
+                       DPRINTF(TRANSLATE,
+                               ("Translate file extent "
+                                "failed: past eof\n"));
+                       UDF_UNLOCK_NODE(udf_node, 0);
+                       return EINVAL;
+               }
+       
+               len    = udf_rw32(s_ad.len);
+               flags  = UDF_EXT_FLAGS(len);
+               len    = UDF_EXT_LEN(len);
+
+               lb_num    = udf_rw32(s_ad.loc.lb_num);
+               vpart_num = udf_rw16(s_ad.loc.part_num);
+
+               end_foffset = foffset + len;
+
+               /* process extent, don't forget to advance on ext_offset! */
+               lb_num  += (ext_offset + lb_size -1) / lb_size;
+               overlap  = (len - ext_offset + lb_size -1) / lb_size;
+               ext_offset = 0;
+
+               /*
+                * note that the while(){} is nessisary for the extent that
+                * the udf_translate_vtop() returns doens't have to span the
+                * whole extent.
+                */
+       
+               overlap = MIN(overlap, num_lb);
+               while (overlap && (flags != UDF_EXT_REDIRECT)) {
+                       switch (flags) {
+                       case UDF_EXT_FREE :
+                       case UDF_EXT_ALLOCATED_BUT_NOT_USED :
+                               transsec = UDF_TRANS_ZERO;
+                               translen = overlap;
+                               while (overlap && num_lb && translen) {
+                                       *map++ = transsec;
+                                       lb_num++;
+                                       overlap--; num_lb--; translen--;
+                               }
+                               break;
+                       case UDF_EXT_ALLOCATED :
+                               t_ad.loc.lb_num   = udf_rw32(lb_num);
+                               t_ad.loc.part_num = udf_rw16(vpart_num);
+                               error = udf_translate_vtop(ump,
+                                               &t_ad, &transsec32, &translen);
+                               transsec = transsec32;
+                               if (error) {
+                                       UDF_UNLOCK_NODE(udf_node, 0);
+                                       return error;
+                               }
+                               while (overlap && num_lb && translen) {
+                                       *map++ = transsec;
+                                       lb_num++; transsec++;
+                                       overlap--; num_lb--; translen--;
+                               }
+                               break;
+                       default:
+                               DPRINTF(TRANSLATE,
+                                       ("Translate file extent "
+                                        "failed: bad flags %x\n", flags));
+                               UDF_UNLOCK_NODE(udf_node, 0);
+                               return EINVAL;
+                       }
+               }
+               if (num_lb == 0)
+                       break;
+
+               if (flags != UDF_EXT_REDIRECT)
+                       foffset = end_foffset;
+               slot++;
+       }
+       UDF_UNLOCK_NODE(udf_node, 0);
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_search_free_vatloc(struct udf_mount *ump, uint32_t *lbnumres)
+{
+       uint32_t lb_size, lb_num, lb_map, udf_rw32_lbmap;
+       uint8_t *blob;
+       int entry, chunk, found, error;
+
+       KASSERT(ump);
+       KASSERT(ump->logical_vol);
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       blob = malloc(lb_size, M_UDFTEMP, M_WAITOK);
+
+       /* TODO static allocation of search chunk */
+
+       lb_num = MIN(ump->vat_entries, ump->vat_last_free_lb);
+       found  = 0;
+       error  = 0;
+       entry  = 0;
+       do {
+               chunk = MIN(lb_size, (ump->vat_entries - lb_num) * 4);
+               if (chunk <= 0)
+                       break;
+               /* load in chunk */
+               error = udf_vat_read(ump->vat_node, blob, chunk,
+                               ump->vat_offset + lb_num * 4);
+
+               if (error)
+                       break;
+
+               /* search this chunk */
+               for (entry=0; entry < chunk /4; entry++, lb_num++) {
+                       udf_rw32_lbmap = *((uint32_t *) (blob + entry * 4));
+                       lb_map = udf_rw32(udf_rw32_lbmap);
+                       if (lb_map == 0xffffffff) {
+                               found = 1;
+                               break;
+                       }
+               }
+       } while (!found);
+       if (error) {
+               printf("udf_search_free_vatloc: error reading in vat chunk "
+                       "(lb %d, size %d)\n", lb_num, chunk);
+       }
+
+       if (!found) {
+               /* extend VAT */
+               DPRINTF(WRITE, ("udf_search_free_vatloc: extending\n"));
+               lb_num = ump->vat_entries;
+               ump->vat_entries++;
+       }
+
+       /* mark entry with initialiser just in case */
+       lb_map = udf_rw32(0xfffffffe);
+       udf_vat_write(ump->vat_node, (uint8_t *) &lb_map, 4,
+               ump->vat_offset + lb_num *4);
+       ump->vat_last_free_lb = lb_num;
+
+       free(blob, M_UDFTEMP);
+       *lbnumres = lb_num;
+       return 0;
+}
+
+
+static void
+udf_bitmap_allocate(struct udf_bitmap *bitmap, int ismetadata,
+       uint32_t *num_lb, uint64_t *lmappos)
+{
+       uint32_t offset, lb_num, bit;
+       int32_t  diff;
+       uint8_t *bpos;
+       int pass;
+
+       if (!ismetadata) {
+               /* heuristic to keep the two pointers not too close */
+               diff = bitmap->data_pos - bitmap->metadata_pos;
+               if ((diff >= 0) && (diff < 1024))
+                       bitmap->data_pos = bitmap->metadata_pos + 1024;
+       }
+       offset = ismetadata ? bitmap->metadata_pos : bitmap->data_pos;
+       offset &= ~7;
+       for (pass = 0; pass < 2; pass++) {
+               if (offset >= bitmap->max_offset)
+                       offset = 0;
+
+               while (offset < bitmap->max_offset) {
+                       if (*num_lb == 0)
+                               break;
+
+                       /* use first bit not set */
+                       bpos  = bitmap->bits + offset/8;
+                       bit = ffs(*bpos);       /* returns 0 or 1..8 */
+                       if (bit == 0) {
+                               offset += 8;
+                               continue;
+                       }
+
+                       /* check for ffs overshoot */
+                       if (offset + bit-1 >= bitmap->max_offset) {
+                               offset = bitmap->max_offset;
+                               break;
+                       }
+
+                       DPRINTF(PARANOIA, ("XXX : allocate %d, %p, bit %d\n",
+                               offset + bit -1, bpos, bit-1));
+                       *bpos &= ~(1 << (bit-1));
+                       lb_num = offset + bit-1;
+                       *lmappos++ = lb_num;
+                       *num_lb = *num_lb - 1;
+                       // offset = (offset & ~7);
+               }
+       }
+
+       if (ismetadata) {
+               bitmap->metadata_pos = offset;
+       } else {
+               bitmap->data_pos = offset;
+       }
+}
+
+
+static void
+udf_bitmap_free(struct udf_bitmap *bitmap, uint32_t lb_num, uint32_t num_lb)
+{
+       uint32_t offset;
+       uint32_t bit, bitval;
+       uint8_t *bpos;
+
+       offset = lb_num;
+
+       /* starter bits */
+       bpos = bitmap->bits + offset/8;
+       bit = offset % 8;
+       while ((bit != 0) && (num_lb > 0)) {
+               bitval = (1 << bit);
+               KASSERT((*bpos & bitval) == 0);
+               DPRINTF(PARANOIA, ("XXX : free %d, %p, %d\n",
+                       offset, bpos, bit));
+               *bpos |= bitval;
+               offset++; num_lb--;
+               bit = (bit + 1) % 8;
+       }
+       if (num_lb == 0)
+               return;
+
+       /* whole bytes */
+       KASSERT(bit == 0);
+       bpos = bitmap->bits + offset / 8;
+       while (num_lb >= 8) {
+               KASSERT((*bpos == 0));
+               DPRINTF(PARANOIA, ("XXX : free %d + 8, %p\n", offset, bpos));
+               *bpos = 255;
+               offset += 8; num_lb -= 8;
+               bpos++;
+       }
+
+       /* stop bits */
+       KASSERT(num_lb < 8);
+       bit = 0;
+       while (num_lb > 0) {
+               bitval = (1 << bit);
+               KASSERT((*bpos & bitval) == 0);
+               DPRINTF(PARANOIA, ("XXX : free %d, %p, %d\n",
+                       offset, bpos, bit));
+               *bpos |= bitval;
+               offset++; num_lb--;
+               bit = (bit + 1) % 8;
+       }
+}
+
+
+static uint32_t
+udf_bitmap_check_trunc_free(struct udf_bitmap *bitmap, uint32_t to_trunc)
+{
+       uint32_t seq_free, offset;
+       uint8_t *bpos;
+       uint8_t  bit, bitval;
+
+       DPRINTF(RESERVE, ("\ttrying to trunc %d bits from bitmap\n", to_trunc));
+       offset = bitmap->max_offset - to_trunc;
+
+       /* starter bits (if any) */
+       bpos = bitmap->bits + offset/8;
+       bit = offset % 8;
+       seq_free = 0;
+       while (to_trunc > 0) {
+               seq_free++;
+               bitval = (1 << bit);
+               if (!(*bpos & bitval))
+                       seq_free = 0;
+               offset++; to_trunc--;
+               bit++;
+               if (bit == 8) {
+                       bpos++;
+                       bit = 0;
+               }
+       }
+
+       DPRINTF(RESERVE, ("\tfound %d sequential free bits in bitmap\n", seq_free));
+       return seq_free;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * We check for overall disc space with a margin to prevent critical
+ * conditions.  If disc space is low we try to force a sync() to improve our
+ * estimates.  When confronted with meta-data partition size shortage we know
+ * we have to check if it can be extended and we need to extend it when
+ * needed.
+ *
+ * A 2nd strategy we could use when disc space is getting low on a disc
+ * formatted with a meta-data partition is to see if there are sparse areas in
+ * the meta-data partition and free blocks there for extra data.
+ */
+
+void
+udf_do_reserve_space(struct udf_mount *ump, struct udf_node *udf_node,
+       uint16_t vpart_num, uint32_t num_lb)
+{
+       ump->uncommitted_lbs[vpart_num] += num_lb;
+       if (udf_node)
+               udf_node->uncommitted_lbs += num_lb;
+}
+
+
+void
+udf_do_unreserve_space(struct udf_mount *ump, struct udf_node *udf_node,
+       uint16_t vpart_num, uint32_t num_lb)
+{
+       ump->uncommitted_lbs[vpart_num] -= num_lb;
+       if (ump->uncommitted_lbs[vpart_num] < 0) {
+               DPRINTF(RESERVE, ("UDF: underflow on partition reservation, "
+                       "part %d: %d\n", vpart_num,
+                       ump->uncommitted_lbs[vpart_num]));
+               ump->uncommitted_lbs[vpart_num] = 0;
+       }
+       if (udf_node) {
+               udf_node->uncommitted_lbs -= num_lb;
+               if (udf_node->uncommitted_lbs < 0) {
+                       DPRINTF(RESERVE, ("UDF: underflow of node "
+                               "reservation : %d\n",
+                               udf_node->uncommitted_lbs));
+                       udf_node->uncommitted_lbs = 0;
+               }
+       }
+}
+
+
+int
+udf_reserve_space(struct udf_mount *ump, struct udf_node *udf_node,
+       int udf_c_type, uint16_t vpart_num, uint32_t num_lb, int can_fail)
+{
+       uint64_t freeblks;
+       uint64_t slack;
+       int i, error;
+
+       slack = 0;
+       if (can_fail)
+               slack = UDF_DISC_SLACK;
+
+       error = 0;
+       mutex_enter(&ump->allocate_mutex);
+
+       /* check if there is enough space available */
+       for (i = 0; i < 3; i++) {       /* XXX arbitrary number */
+               udf_calc_vpart_freespace(ump, vpart_num, &freeblks);
+               if (num_lb + slack < freeblks)
+                       break;
+               /* issue SYNC */
+               DPRINTF(RESERVE, ("udf_reserve_space: issuing sync\n"));
+               mutex_exit(&ump->allocate_mutex);
+               udf_do_sync(ump, FSCRED, 0);
+               mutex_enter(&mntvnode_lock);
+               /* 1/8 second wait */
+               cv_timedwait(&ump->dirtynodes_cv, &mntvnode_lock,
+                       hz/8);
+               mutex_exit(&mntvnode_lock);
+               mutex_enter(&ump->allocate_mutex);
+       }
+
+       /* check if there is enough space available now */
+       udf_calc_vpart_freespace(ump, vpart_num, &freeblks);
+       if (num_lb + slack >= freeblks) {
+               DPRINTF(RESERVE, ("udf_reserve_space: try to redistribute "
+                                 "partition space\n"));
+               DPRINTF(RESERVE, ("\tvpart %d, type %d is full\n",
+                               vpart_num, ump->vtop_alloc[vpart_num]));
+               /* Try to redistribute space if possible */
+               udf_collect_free_space_for_vpart(ump, vpart_num, num_lb + slack);
+       }
+
+       /* check if there is enough space available now */
+       udf_calc_vpart_freespace(ump, vpart_num, &freeblks);
+       if (num_lb + slack <= freeblks) {
+               udf_do_reserve_space(ump, udf_node, vpart_num, num_lb);
+       } else {
+               DPRINTF(RESERVE, ("udf_reserve_space: out of disc space\n"));
+               error = ENOSPC;
+       }
+
+       mutex_exit(&ump->allocate_mutex);
+       return error;
+}
+
+
+void
+udf_cleanup_reservation(struct udf_node *udf_node)
+{
+       struct udf_mount *ump = udf_node->ump;
+       int vpart_num;
+
+       mutex_enter(&ump->allocate_mutex);
+
+       /* compensate for overlapping blocks */
+       DPRINTF(RESERVE, ("UDF: overlapped %d blocks in count\n", udf_node->uncommitted_lbs));
+
+       vpart_num = udf_get_record_vpart(ump, udf_get_c_type(udf_node));
+       udf_do_unreserve_space(ump, udf_node, vpart_num, udf_node->uncommitted_lbs);
+
+       DPRINTF(RESERVE, ("\ttotal now %d\n", ump->uncommitted_lbs[vpart_num]));
+
+       /* sanity */
+       if (ump->uncommitted_lbs[vpart_num] < 0)
+               ump->uncommitted_lbs[vpart_num] = 0;
+
+       mutex_exit(&ump->allocate_mutex);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Allocate an extent of given length on given virt. partition. It doesn't
+ * have to be one stretch.
+ */
+
+int
+udf_allocate_space(struct udf_mount *ump, struct udf_node *udf_node,
+       int udf_c_type, uint16_t vpart_num, uint32_t num_lb, uint64_t *lmapping)
+{
+       struct mmc_trackinfo *alloc_track, *other_track;
+       struct udf_bitmap *bitmap;
+       struct part_desc *pdesc;
+       struct logvol_int_desc *lvid;
+       uint64_t *lmappos;
+       uint32_t ptov, lb_num, *freepos, free_lbs;
+       int lb_size __diagused, alloc_num_lb;
+       int alloc_type, error;
+       int is_node;
+
+       DPRINTF(CALL, ("udf_allocate_space(ctype %d, vpart %d, num_lb %d\n",
+               udf_c_type, vpart_num, num_lb));
+       mutex_enter(&ump->allocate_mutex);
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       KASSERT(lb_size == ump->discinfo.sector_size);
+
+       alloc_type =  ump->vtop_alloc[vpart_num];
+       is_node    = (udf_c_type == UDF_C_NODE);
+
+       lmappos = lmapping;
+       error = 0;
+       switch (alloc_type) {
+       case UDF_ALLOC_VAT :
+               /* search empty slot in VAT file */
+               KASSERT(num_lb == 1);
+               error = udf_search_free_vatloc(ump, &lb_num);
+               if (!error) {
+                       *lmappos = lb_num;
+
+                       /* reserve on the backing sequential partition since
+                        * that partition is credited back later */
+                       udf_do_reserve_space(ump, udf_node,
+                               ump->vtop[vpart_num], num_lb);
+               }
+               break;
+       case UDF_ALLOC_SEQUENTIAL :
+               /* sequential allocation on recordable media */
+               /* get partition backing up this vpart_num_num */
+               pdesc = ump->partitions[ump->vtop[vpart_num]];
+
+               /* calculate offset from physical base partition */
+               ptov  = udf_rw32(pdesc->start_loc);
+
+               /* get our track descriptors */
+               if (vpart_num == ump->node_part) {
+                       alloc_track = &ump->metadata_track;
+                       other_track = &ump->data_track;
+               } else {
+                       alloc_track = &ump->data_track;
+                       other_track = &ump->metadata_track;
+               }
+
+               /* allocate */
+               for (lb_num = 0; lb_num < num_lb; lb_num++) {
+                       *lmappos++ = alloc_track->next_writable - ptov;
+                       alloc_track->next_writable++;
+                       alloc_track->free_blocks--;
+               }
+
+               /* keep other track up-to-date */
+               if (alloc_track->tracknr == other_track->tracknr)
+                       memcpy(other_track, alloc_track,
+                               sizeof(struct mmc_trackinfo));
+               break;
+       case UDF_ALLOC_SPACEMAP :
+               /* try to allocate on unallocated bits */
+               alloc_num_lb = num_lb;
+               bitmap = &ump->part_unalloc_bits[vpart_num];
+               udf_bitmap_allocate(bitmap, is_node, &alloc_num_lb, lmappos);
+               ump->lvclose |= UDF_WRITE_PART_BITMAPS;
+
+               /* have we allocated all? */
+               if (alloc_num_lb) {
+                       /* TODO convert freed to unalloc and try again */
+                       /* free allocated piece for now */
+                       lmappos = lmapping;
+                       for (lb_num=0; lb_num < num_lb-alloc_num_lb; lb_num++) {
+                               udf_bitmap_free(bitmap, *lmappos++, 1);
+                       }
+                       error = ENOSPC;
+               }
+               if (!error) {
+                       /* adjust freecount */
+                       lvid = ump->logvol_integrity;
+                       freepos = &lvid->tables[0] + vpart_num;
+                       free_lbs = udf_rw32(*freepos);
+                       *freepos = udf_rw32(free_lbs - num_lb);
+               }
+               break;
+       case UDF_ALLOC_METABITMAP :             /* UDF 2.50, 2.60 BluRay-RE */
+               /* allocate on metadata unallocated bits */
+               alloc_num_lb = num_lb;
+               bitmap = &ump->metadata_unalloc_bits;
+               udf_bitmap_allocate(bitmap, is_node, &alloc_num_lb, lmappos);
+               ump->lvclose |= UDF_WRITE_PART_BITMAPS;
+
+               /* have we allocated all? */
+               if (alloc_num_lb) {
+                       /* YIKES! TODO we need to extend the metadata partition */
+                       /* free allocated piece for now */
+                       lmappos = lmapping;
+                       for (lb_num=0; lb_num < num_lb-alloc_num_lb; lb_num++) {
+                               udf_bitmap_free(bitmap, *lmappos++, 1);
+                       }
+                       error = ENOSPC;
+               }
+               if (!error) {
+                       /* adjust freecount */
+                       lvid = ump->logvol_integrity;
+                       freepos = &lvid->tables[0] + vpart_num;
+                       free_lbs = udf_rw32(*freepos);
+                       *freepos = udf_rw32(free_lbs - num_lb);
+               }
+               break;
+       case UDF_ALLOC_METASEQUENTIAL :         /* UDF 2.60       BluRay-R  */
+       case UDF_ALLOC_RELAXEDSEQUENTIAL :      /* UDF 2.50/~meta BluRay-R  */
+               printf("ALERT: udf_allocate_space : allocation %d "
+                               "not implemented yet!\n", alloc_type);
+               /* TODO implement, doesn't have to be contiguous */
+               error = ENOSPC;
+               break;
+       }
+
+       if (!error) {
+               /* credit our partition since we have committed the space */
+               udf_do_unreserve_space(ump, udf_node, vpart_num, num_lb);
+       }
+
+#ifdef DEBUG
+       if (udf_verbose & UDF_DEBUG_ALLOC) {
+               lmappos = lmapping;
+               printf("udf_allocate_space, allocated logical lba :\n");
+               for (lb_num = 0; lb_num < num_lb; lb_num++) {
+                       printf("%s %"PRIu64, (lb_num > 0)?",":"", 
+                               *lmappos++);
+               }
+               printf("\n");
+       }
+#endif
+       mutex_exit(&ump->allocate_mutex);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+void
+udf_free_allocated_space(struct udf_mount *ump, uint32_t lb_num,
+       uint16_t vpart_num, uint32_t num_lb)
+{
+       struct udf_bitmap *bitmap;
+       struct logvol_int_desc *lvid;
+       uint32_t lb_map, udf_rw32_lbmap;
+       uint32_t *freepos, free_lbs;
+       int phys_part;
+       int error __diagused;
+
+       DPRINTF(ALLOC, ("udf_free_allocated_space: freeing virt lbnum %d "
+                         "part %d + %d sect\n", lb_num, vpart_num, num_lb));
+
+       /* no use freeing zero length */
+       if (num_lb == 0)
+               return;
+
+       mutex_enter(&ump->allocate_mutex);
+
+       switch (ump->vtop_tp[vpart_num]) {
+       case UDF_VTOP_TYPE_PHYS :
+       case UDF_VTOP_TYPE_SPARABLE :
+               /* free space to freed or unallocated space bitmap */
+               phys_part = ump->vtop[vpart_num];
+
+               /* first try freed space bitmap */
+               bitmap    = &ump->part_freed_bits[phys_part];
+
+               /* if not defined, use unallocated bitmap */
+               if (bitmap->bits == NULL)
+                       bitmap = &ump->part_unalloc_bits[phys_part];
+
+               /* if no bitmaps are defined, bail out; XXX OK? */
+               if (bitmap->bits == NULL)
+                       break;
+
+               /* free bits if its defined */
+               KASSERT(bitmap->bits);
+               ump->lvclose |= UDF_WRITE_PART_BITMAPS;
+               udf_bitmap_free(bitmap, lb_num, num_lb);
+
+               /* adjust freecount */
+               lvid = ump->logvol_integrity;
+               freepos = &lvid->tables[0] + vpart_num;
+               free_lbs = udf_rw32(*freepos);
+               *freepos = udf_rw32(free_lbs + num_lb);
+               break;
+       case UDF_VTOP_TYPE_VIRT :
+               /* free this VAT entry */
+               KASSERT(num_lb == 1);
+
+               lb_map = 0xffffffff;
+               udf_rw32_lbmap = udf_rw32(lb_map);
+               error = udf_vat_write(ump->vat_node,
+                       (uint8_t *) &udf_rw32_lbmap, 4,
+                       ump->vat_offset + lb_num * 4);
+               KASSERT(error == 0);
+               ump->vat_last_free_lb = MIN(ump->vat_last_free_lb, lb_num);
+               break;
+       case UDF_VTOP_TYPE_META :
+               /* free space in the metadata bitmap */
+               bitmap = &ump->metadata_unalloc_bits;
+               KASSERT(bitmap->bits);
+
+               ump->lvclose |= UDF_WRITE_PART_BITMAPS;
+               udf_bitmap_free(bitmap, lb_num, num_lb);
+
+               /* adjust freecount */
+               lvid = ump->logvol_integrity;
+               freepos = &lvid->tables[0] + vpart_num;
+               free_lbs = udf_rw32(*freepos);
+               *freepos = udf_rw32(free_lbs + num_lb);
+               break;
+       default:
+               printf("ALERT: udf_free_allocated_space : allocation %d "
+                       "not implemented yet!\n", ump->vtop_tp[vpart_num]);
+               break;
+       }
+
+       mutex_exit(&ump->allocate_mutex);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Special function to synchronise the metadatamirror file when they change on
+ * resizing. When the metadatafile is actually duplicated, this action is a
+ * no-op since they describe different extents on the disc.
+ */
+
+void
+udf_synchronise_metadatamirror_node(struct udf_mount *ump)
+{
+       struct udf_node *meta_node, *metamirror_node;
+       struct long_ad s_ad;
+       uint32_t len, flags;
+       int slot, cpy_slot;
+       int error, eof;
+
+       if (ump->metadata_flags & METADATA_DUPLICATED)
+               return;
+
+       meta_node       = ump->metadata_node;
+       metamirror_node = ump->metadatamirror_node;
+
+       /* 1) wipe mirror node */
+       udf_wipe_adslots(metamirror_node);
+
+       /* 2) copy all node descriptors from the meta_node */
+       slot     = 0;
+       cpy_slot = 0;
+       for (;;) {
+               udf_get_adslot(meta_node, slot, &s_ad, &eof);
+               if (eof)
+                       break;
+               len   = udf_rw32(s_ad.len);
+               flags = UDF_EXT_FLAGS(len);
+               len   = UDF_EXT_LEN(len);
+
+               if (flags == UDF_EXT_REDIRECT) {
+                       slot++;
+                       continue;
+               }
+
+               error = udf_append_adslot(metamirror_node, &cpy_slot, &s_ad);
+               if (error) {
+                       /* WTF, this shouldn't happen, what to do now? */
+                       panic("udf_synchronise_metadatamirror_node failed!");
+               }
+               cpy_slot++;
+               slot++;
+       }
+
+       /* 3) adjust metamirror_node size */
+       if (meta_node->fe) {
+               KASSERT(metamirror_node->fe);
+               metamirror_node->fe->inf_len = meta_node->fe->inf_len;
+       } else {
+               KASSERT(meta_node->efe);
+               KASSERT(metamirror_node->efe);
+               metamirror_node->efe->inf_len  = meta_node->efe->inf_len;
+               metamirror_node->efe->obj_size = meta_node->efe->obj_size;
+       }
+
+       /* for sanity */
+       udf_count_alloc_exts(metamirror_node);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * When faced with an out of space but there is still space available on other
+ * partitions, try to redistribute the space. This is only defined for media
+ * using Metadata partitions.
+ *
+ * There are two formats to deal with. Either its a `normal' metadata
+ * partition and we can move blocks between a metadata bitmap and its
+ * companion data spacemap OR its a UDF 2.60 formatted BluRay-R disc with POW
+ * and a metadata partition.
+ */
+
+/* implementation limit: ump->datapart is the companion partition */
+static uint32_t
+udf_trunc_metadatapart(struct udf_mount *ump, uint32_t num_lb)
+{
+       struct udf_node *bitmap_node;
+       struct udf_bitmap *bitmap;
+       struct space_bitmap_desc *sbd, *new_sbd;
+       struct logvol_int_desc *lvid;
+       uint64_t inf_len;
+       uint64_t meta_free_lbs, data_free_lbs, to_trunc;
+       uint32_t *freepos, *sizepos;
+       uint32_t unit, lb_size;
+       uint16_t meta_vpart_num, data_vpart_num, num_vpart;
+       int err __diagused;
+
+       unit = ump->metadata_alloc_unit_size;
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       lvid = ump->logvol_integrity;
+
+       /* XXX
+        *
+        * the following checks will fail for BD-R UDF 2.60! but they are
+        * read-only for now anyway! Its even doubtfull if it is to be allowed
+        * for these discs.
+        */
+
+       /* lookup vpart for metadata partition */
+       meta_vpart_num = ump->node_part;
+       KASSERT(ump->vtop_alloc[meta_vpart_num] == UDF_ALLOC_METABITMAP);
+
+       /* lookup vpart for data partition */
+       data_vpart_num = ump->data_part;
+       KASSERT(ump->vtop_alloc[data_vpart_num] == UDF_ALLOC_SPACEMAP);
+
+       udf_calc_vpart_freespace(ump, data_vpart_num, &data_free_lbs);
+       udf_calc_vpart_freespace(ump, meta_vpart_num, &meta_free_lbs);
+
+       DPRINTF(RESERVE, ("\tfree space on data partition     %"PRIu64" blks\n", data_free_lbs));
+       DPRINTF(RESERVE, ("\tfree space on metadata partition %"PRIu64" blks\n", meta_free_lbs));
+
+       /* give away some of the free meta space, in unit block sizes */
+       to_trunc = meta_free_lbs/4;                     /* give out a quarter */
+       to_trunc = MAX(to_trunc, num_lb);
+       to_trunc = unit * ((to_trunc + unit-1) / unit); /* round up */
+
+       /* scale down if needed and bail out when out of space */
+       if (to_trunc >= meta_free_lbs)
+               return num_lb;
+
+       /* check extent of bits marked free at the end of the map */
+       bitmap = &ump->metadata_unalloc_bits;
+       to_trunc = udf_bitmap_check_trunc_free(bitmap, to_trunc);
+       to_trunc = unit * (to_trunc / unit);            /* round down again */
+       if (to_trunc == 0)
+               return num_lb;
+
+       DPRINTF(RESERVE, ("\ttruncating %"PRIu64" lbs from the metadata bitmap\n",
+               to_trunc));
+
+       /* get length of the metadata bitmap node file */
+       bitmap_node = ump->metadatabitmap_node;
+       if (bitmap_node->fe) {
+               inf_len = udf_rw64(bitmap_node->fe->inf_len);
+       } else {
+               KASSERT(bitmap_node->efe);
+               inf_len = udf_rw64(bitmap_node->efe->inf_len);
+       }
+       inf_len -= to_trunc/8;
+
+       /* as per [UDF 2.60/2.2.13.6] : */
+       /* 1) update the SBD in the metadata bitmap file */
+       sbd = (struct space_bitmap_desc *) bitmap->blob;
+       sbd->num_bits  = udf_rw32(udf_rw32(sbd->num_bits)  - to_trunc);
+       sbd->num_bytes = udf_rw32(udf_rw32(sbd->num_bytes) - to_trunc/8);
+       bitmap->max_offset = udf_rw32(sbd->num_bits);
+
+       num_vpart = udf_rw32(lvid->num_part);
+       freepos = &lvid->tables[0] + meta_vpart_num;
+       sizepos = &lvid->tables[0] + num_vpart + meta_vpart_num;
+       *freepos = udf_rw32(*freepos) - to_trunc;
+       *sizepos = udf_rw32(*sizepos) - to_trunc;
+
+       /* realloc bitmap for better memory usage */
+       new_sbd = realloc(sbd, inf_len, M_UDFVOLD,
+               M_CANFAIL | M_WAITOK);
+       if (new_sbd) {
+               /* update pointers */
+               ump->metadata_unalloc_dscr = new_sbd;
+               bitmap->blob = (uint8_t *) new_sbd;
+       }
+       ump->lvclose |= UDF_WRITE_PART_BITMAPS;
+
+       /*
+        * The truncated space is secured now and can't be allocated anymore.
+        * Release the allocate mutex so we can shrink the nodes the normal
+        * way.
+        */
+       mutex_exit(&ump->allocate_mutex);
+
+       /* 2) trunc the metadata bitmap information file, freeing blocks */
+       err = udf_shrink_node(bitmap_node, inf_len);
+       KASSERT(err == 0);
+
+       /* 3) trunc the metadata file and mirror file, freeing blocks */
+       inf_len = (uint64_t) udf_rw32(sbd->num_bits) * lb_size; /* [4/14.12.4] */
+       err = udf_shrink_node(ump->metadata_node, inf_len);
+       KASSERT(err == 0);
+       if (ump->metadatamirror_node) {
+               if (ump->metadata_flags & METADATA_DUPLICATED) {
+                       err = udf_shrink_node(ump->metadatamirror_node, inf_len);
+               } else {
+                       /* extents will be copied on writeout */
+               }
+               KASSERT(err == 0);
+       }
+       ump->lvclose |= UDF_WRITE_METAPART_NODES;
+
+       /* relock before exit */
+       mutex_enter(&ump->allocate_mutex);
+
+       if (to_trunc > num_lb)
+               return 0;
+       return num_lb - to_trunc;
+}
+
+
+static void
+udf_sparsify_metadatapart(struct udf_mount *ump, uint32_t num_lb)
+{
+       /* NOT IMPLEMENTED, fail */
+}
+
+
+static void
+udf_collect_free_space_for_vpart(struct udf_mount *ump,
+       uint16_t vpart_num, uint32_t num_lb)
+{
+       /* allocate mutex is helt */
+
+       /* only defined for metadata partitions */
+       if (ump->vtop_tp[ump->node_part] != UDF_VTOP_TYPE_META) {
+               DPRINTF(RESERVE, ("\tcan't grow/shrink; no metadata partitioning\n"));
+               return;
+       }
+
+       /* UDF 2.60 BD-R+POW? */
+       if (ump->vtop_alloc[ump->node_part] == UDF_ALLOC_METASEQUENTIAL) {
+               DPRINTF(RESERVE, ("\tUDF 2.60 BD-R+POW track grow not implemented yet\n"));
+               return;
+       }
+
+       if (ump->vtop_tp[vpart_num] == UDF_VTOP_TYPE_META) {
+               /* try to grow the meta partition */
+               DPRINTF(RESERVE, ("\ttrying to grow the meta partition\n"));
+               /* as per [UDF 2.60/2.2.13.5] : extend bitmap and metadata file(s) */
+               DPRINTF(NOTIMPL, ("\tgrowing meta partition not implemented yet\n"));
+       } else {
+               /* try to shrink the metadata partition */
+               DPRINTF(RESERVE, ("\ttrying to shrink the meta partition\n"));
+               /* as per [UDF 2.60/2.2.13.6] : either trunc or make sparse */
+               num_lb = udf_trunc_metadatapart(ump, num_lb);
+               if (num_lb)
+                       udf_sparsify_metadatapart(ump, num_lb);
+       }
+
+       /* allocate mutex should still be helt */
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Allocate a buf on disc for direct write out. The space doesn't have to be
+ * contiguous as the caller takes care of this.
+ */
+
+void
+udf_late_allocate_buf(struct udf_mount *ump, struct buf *buf,
+       uint64_t *lmapping, struct long_ad *node_ad_cpy, uint16_t *vpart_nump)
+{
+       struct udf_node  *udf_node = VTOI(buf->b_vp);
+       int lb_size, udf_c_type;
+       int vpart_num, num_lb;
+       int error, s;
+
+       /*
+        * for each sector in the buf, allocate a sector on disc and record
+        * its position in the provided mapping array.
+        *
+        * If its userdata or FIDs, record its location in its node.
+        */
+
+       lb_size    = udf_rw32(ump->logical_vol->lb_size);
+       num_lb     = (buf->b_bcount + lb_size -1) / lb_size;
+       udf_c_type = buf->b_udf_c_type;
+
+       KASSERT(lb_size == ump->discinfo.sector_size);
+
+       /* select partition to record the buffer on */
+       vpart_num = *vpart_nump = udf_get_record_vpart(ump, udf_c_type);
+
+       if (udf_c_type == UDF_C_NODE) {
+               /* if not VAT, its allready allocated */
+               if (ump->vtop_alloc[ump->node_part] != UDF_ALLOC_VAT)
+                       return;
+
+               /* allocate on its backing sequential partition */
+               vpart_num = ump->data_part;
+       }
+
+       /* XXX can this still happen? */
+       /* do allocation on the selected partition */
+       error = udf_allocate_space(ump, udf_node, udf_c_type,
+                       vpart_num, num_lb, lmapping);
+       if (error) {
+               /*
+                * ARGH! we haven't done our accounting right! it should
+                * allways succeed.
+                */
+               panic("UDF disc allocation accounting gone wrong");
+       }
+
+       /* If its userdata or FIDs, record its allocation in its node. */
+       if ((udf_c_type == UDF_C_USERDATA) ||
+           (udf_c_type == UDF_C_FIDS) ||
+           (udf_c_type == UDF_C_METADATA_SBM))
+       {
+               udf_record_allocation_in_node(ump, buf, vpart_num, lmapping,
+                       node_ad_cpy);
+               /* decrement our outstanding bufs counter */
+               s = splbio();
+                       udf_node->outstanding_bufs--;
+               splx(s);
+       }
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Try to merge a1 with the new piece a2. udf_ads_merge returns error when not
+ * possible (anymore); a2 returns the rest piece.
+ */
+
+static int
+udf_ads_merge(uint32_t max_len, uint32_t lb_size, struct long_ad *a1, struct long_ad *a2)
+{
+       uint32_t merge_len;
+       uint32_t a1_len, a2_len;
+       uint32_t a1_flags, a2_flags;
+       uint32_t a1_lbnum, a2_lbnum;
+       uint16_t a1_part, a2_part;
+
+       a1_flags = UDF_EXT_FLAGS(udf_rw32(a1->len));
+       a1_len   = UDF_EXT_LEN(udf_rw32(a1->len));
+       a1_lbnum = udf_rw32(a1->loc.lb_num);
+       a1_part  = udf_rw16(a1->loc.part_num);
+
+       a2_flags = UDF_EXT_FLAGS(udf_rw32(a2->len));
+       a2_len   = UDF_EXT_LEN(udf_rw32(a2->len));
+       a2_lbnum = udf_rw32(a2->loc.lb_num);
+       a2_part  = udf_rw16(a2->loc.part_num);
+
+       /* defines same space */
+       if (a1_flags != a2_flags)
+               return 1;
+
+       if (a1_flags != UDF_EXT_FREE) {
+               /* the same partition */
+               if (a1_part != a2_part)
+                       return 1;
+
+               /* a2 is successor of a1 */
+               if (a1_lbnum * lb_size + a1_len != a2_lbnum * lb_size)
+                       return 1;
+       }
+
+       /* merge as most from a2 if possible */
+       merge_len = MIN(a2_len, max_len - a1_len);
+       a1_len   += merge_len;
+       a2_len   -= merge_len;
+       a2_lbnum += merge_len/lb_size;
+
+       a1->len = udf_rw32(a1_len | a1_flags);
+       a2->len = udf_rw32(a2_len | a2_flags);
+       a2->loc.lb_num = udf_rw32(a2_lbnum);
+
+       if (a2_len > 0)
+               return 1;
+
+       /* there is space over to merge */
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void
+udf_wipe_adslots(struct udf_node *udf_node)
+{
+       struct file_entry      *fe;
+       struct extfile_entry   *efe;
+       struct alloc_ext_entry *ext;
+       uint32_t lb_size, dscr_size, l_ea, max_l_ad, crclen;
+       uint8_t *data_pos;
+       int extnr;
+
+       lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
+
+       fe  = udf_node->fe;
+       efe = udf_node->efe;
+       if (fe) {
+               dscr_size  = sizeof(struct file_entry) -1;
+               l_ea       = udf_rw32(fe->l_ea);
+               data_pos = (uint8_t *) fe + dscr_size + l_ea;
+       } else {
+               dscr_size  = sizeof(struct extfile_entry) -1;
+               l_ea       = udf_rw32(efe->l_ea);
+               data_pos = (uint8_t *) efe + dscr_size + l_ea;
+       }
+       max_l_ad = lb_size - dscr_size - l_ea;
+
+       /* wipe fe/efe */
+       memset(data_pos, 0, max_l_ad);
+       crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea;
+       if (fe) {
+               fe->l_ad         = udf_rw32(0);
+               fe->logblks_rec  = udf_rw64(0);
+               fe->tag.desc_crc_len = udf_rw16(crclen);
+       } else {
+               efe->l_ad        = udf_rw32(0);
+               efe->logblks_rec = udf_rw64(0);
+               efe->tag.desc_crc_len = udf_rw16(crclen);
+       }
+
+       /* wipe all allocation extent entries */
+       for (extnr = 0; extnr < udf_node->num_extensions; extnr++) {
+               ext = udf_node->ext[extnr];
+               dscr_size  = sizeof(struct alloc_ext_entry) -1;
+               data_pos = (uint8_t *) ext->data;
+               max_l_ad = lb_size - dscr_size;
+               memset(data_pos, 0, max_l_ad);
+               ext->l_ad = udf_rw32(0);
+
+               crclen = dscr_size - UDF_DESC_TAG_LENGTH;
+               ext->tag.desc_crc_len = udf_rw16(crclen);
+       }
+       udf_node->i_flags |= IN_NODE_REBUILD;
+}
+
+/* --------------------------------------------------------------------- */
+
+void
+udf_get_adslot(struct udf_node *udf_node, int slot, struct long_ad *icb,
+       int *eof) {
+       struct file_entry      *fe;
+       struct extfile_entry   *efe;
+       struct alloc_ext_entry *ext;
+       struct icb_tag *icbtag;
+       struct short_ad *short_ad;
+       struct long_ad *long_ad, l_icb;
+       uint32_t offset;
+       uint32_t dscr_size, l_ea, l_ad, flags;
+       uint8_t *data_pos;
+       int icbflags, addr_type, adlen, extnr;
+
+       fe  = udf_node->fe;
+       efe = udf_node->efe;
+       if (fe) {
+               icbtag  = &fe->icbtag;
+               dscr_size  = sizeof(struct file_entry) -1;
+               l_ea       = udf_rw32(fe->l_ea);
+               l_ad       = udf_rw32(fe->l_ad);
+               data_pos = (uint8_t *) fe + dscr_size + l_ea;
+       } else {
+               icbtag  = &efe->icbtag;
+               dscr_size  = sizeof(struct extfile_entry) -1;
+               l_ea       = udf_rw32(efe->l_ea);
+               l_ad       = udf_rw32(efe->l_ad);
+               data_pos = (uint8_t *) efe + dscr_size + l_ea;
+       }
+
+       icbflags  = udf_rw16(icbtag->flags);
+       addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       /* just in case we're called on an intern, its EOF */
+       if (addr_type == UDF_ICB_INTERN_ALLOC) {
+               memset(icb, 0, sizeof(struct long_ad));
+               *eof = 1;
+               return;
+       }
+
+       adlen = 0;
+       if (addr_type == UDF_ICB_SHORT_ALLOC) {
+               adlen = sizeof(struct short_ad);
+       } else if (addr_type == UDF_ICB_LONG_ALLOC) {
+               adlen = sizeof(struct long_ad);
+       }
+
+       /* if offset too big, we go to the allocation extensions */
+       offset = slot * adlen;
+       extnr  = -1;
+       while (offset >= l_ad) {
+               /* check if our last entry is a redirect */
+               if (addr_type == UDF_ICB_SHORT_ALLOC) {
+                       short_ad = (struct short_ad *) (data_pos + l_ad-adlen);
+                       l_icb.len          = short_ad->len;
+                       l_icb.loc.part_num = udf_node->loc.loc.part_num;
+                       l_icb.loc.lb_num   = short_ad->lb_num;
+               } else {
+                       KASSERT(addr_type == UDF_ICB_LONG_ALLOC);
+                       long_ad = (struct long_ad *) (data_pos + l_ad-adlen);
+                       l_icb = *long_ad;
+               }
+               flags = UDF_EXT_FLAGS(udf_rw32(l_icb.len));
+               if (flags != UDF_EXT_REDIRECT) {
+                       l_ad = 0;       /* force EOF */
+                       break;
+               }
+
+               /* advance to next extent */
+               extnr++;
+               if (extnr >= udf_node->num_extensions) {
+                       l_ad = 0;       /* force EOF */
+                       break;
+               }
+               offset = offset - l_ad;
+               ext  = udf_node->ext[extnr];
+               dscr_size  = sizeof(struct alloc_ext_entry) -1;
+               l_ad = udf_rw32(ext->l_ad);
+               data_pos = (uint8_t *) ext + dscr_size;
+       }
+
+       /* XXX l_ad == 0 should be enough to check */
+       *eof = (offset >= l_ad) || (l_ad == 0);
+       if (*eof) {
+               DPRINTF(PARANOIDADWLK, ("returning EOF, extnr %d, offset %d, "
+                       "l_ad %d\n", extnr, offset, l_ad));
+               memset(icb, 0, sizeof(struct long_ad));
+               return;
+       }
+
+       /* get the element */
+       if (addr_type == UDF_ICB_SHORT_ALLOC) {
+               short_ad = (struct short_ad *) (data_pos + offset);
+               icb->len          = short_ad->len;
+               icb->loc.part_num = udf_node->loc.loc.part_num;
+               icb->loc.lb_num   = short_ad->lb_num;
+       } else if (addr_type == UDF_ICB_LONG_ALLOC) {
+               long_ad = (struct long_ad *) (data_pos + offset);
+               *icb = *long_ad;
+       }
+       DPRINTF(PARANOIDADWLK, ("returning element : v %d, lb %d, len %d, "
+               "flags %d\n", icb->loc.part_num, icb->loc.lb_num,
+               UDF_EXT_LEN(icb->len), UDF_EXT_FLAGS(icb->len)));
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_append_adslot(struct udf_node *udf_node, int *slot, struct long_ad *icb) {
+       struct udf_mount *ump = udf_node->ump;
+       union dscrptr          *dscr, *extdscr;
+       struct file_entry      *fe;
+       struct extfile_entry   *efe;
+       struct alloc_ext_entry *ext;
+       struct icb_tag *icbtag;
+       struct short_ad *short_ad;
+       struct long_ad *long_ad, o_icb, l_icb;
+       uint64_t logblks_rec, *logblks_rec_p;
+       uint64_t lmapping;
+       uint32_t offset, rest, len, lb_num;
+       uint32_t lb_size, dscr_size, l_ea, l_ad, *l_ad_p, max_l_ad, crclen;
+       uint32_t flags;
+       uint16_t vpart_num;
+       uint8_t *data_pos;
+       int icbflags, addr_type, adlen, extnr;
+       int error;
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       vpart_num = udf_rw16(udf_node->loc.loc.part_num);
+
+       /* determine what descriptor we are in */
+       fe  = udf_node->fe;
+       efe = udf_node->efe;
+       if (fe) {
+               icbtag  = &fe->icbtag;
+               dscr      = (union dscrptr *) fe;
+               dscr_size = sizeof(struct file_entry) -1;
+
+               l_ea      = udf_rw32(fe->l_ea);
+               l_ad_p    = &fe->l_ad;
+               logblks_rec_p = &fe->logblks_rec;
+       } else {
+               icbtag    = &efe->icbtag;
+               dscr      = (union dscrptr *) efe;
+               dscr_size = sizeof(struct extfile_entry) -1;
+
+               l_ea      = udf_rw32(efe->l_ea);
+               l_ad_p    = &efe->l_ad;
+               logblks_rec_p = &efe->logblks_rec;
+       }
+       data_pos  = (uint8_t *) dscr + dscr_size + l_ea;
+       max_l_ad = lb_size - dscr_size - l_ea;
+
+       icbflags  = udf_rw16(icbtag->flags);
+       addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       /* just in case we're called on an intern, its EOF */
+       if (addr_type == UDF_ICB_INTERN_ALLOC) {
+               panic("udf_append_adslot on UDF_ICB_INTERN_ALLOC\n");
+       }
+
+       adlen = 0;
+       if (addr_type == UDF_ICB_SHORT_ALLOC) {
+               adlen = sizeof(struct short_ad);
+       } else if (addr_type == UDF_ICB_LONG_ALLOC) {
+               adlen = sizeof(struct long_ad);
+       }
+
+       /* clean up given long_ad since it can be a synthesized one */
+       flags = UDF_EXT_FLAGS(udf_rw32(icb->len));
+       if (flags == UDF_EXT_FREE) {
+               icb->loc.part_num = udf_rw16(0);
+               icb->loc.lb_num   = udf_rw32(0);
+       }
+
+       /* if offset too big, we go to the allocation extensions */
+       l_ad   = udf_rw32(*l_ad_p);
+       offset = (*slot) * adlen;
+       extnr  = -1;
+       while (offset >= l_ad) {
+               /* check if our last entry is a redirect */
+               if (addr_type == UDF_ICB_SHORT_ALLOC) {
+                       short_ad = (struct short_ad *) (data_pos + l_ad-adlen);
+                       l_icb.len          = short_ad->len;
+                       l_icb.loc.part_num = udf_node->loc.loc.part_num;
+                       l_icb.loc.lb_num   = short_ad->lb_num;
+               } else {
+                       KASSERT(addr_type == UDF_ICB_LONG_ALLOC);
+                       long_ad = (struct long_ad *) (data_pos + l_ad-adlen);
+                       l_icb = *long_ad;
+               }
+               flags = UDF_EXT_FLAGS(udf_rw32(l_icb.len));
+               if (flags != UDF_EXT_REDIRECT) {
+                       /* only one past the last one is adressable */
+                       break;
+               }
+
+               /* advance to next extent */
+               extnr++;
+               KASSERT(extnr < udf_node->num_extensions);
+               offset = offset - l_ad;
+
+               ext  = udf_node->ext[extnr];
+               dscr = (union dscrptr *) ext;
+               dscr_size  = sizeof(struct alloc_ext_entry) -1;
+               max_l_ad = lb_size - dscr_size;
+               l_ad_p = &ext->l_ad;
+               l_ad   = udf_rw32(*l_ad_p);
+               data_pos = (uint8_t *) ext + dscr_size;
+       }
+       DPRINTF(PARANOIDADWLK, ("append, ext %d, offset %d, l_ad %d\n",
+               extnr, offset, udf_rw32(*l_ad_p)));
+       KASSERT(l_ad == udf_rw32(*l_ad_p));
+
+       /* offset is offset within the current (E)FE/AED */
+       l_ad   = udf_rw32(*l_ad_p);
+       crclen = udf_rw16(dscr->tag.desc_crc_len);
+       logblks_rec = udf_rw64(*logblks_rec_p);
+
+       /* overwriting old piece? */
+       if (offset < l_ad) {
+               /* overwrite entry; compensate for the old element */
+               if (addr_type == UDF_ICB_SHORT_ALLOC) {
+                       short_ad = (struct short_ad *) (data_pos + offset);
+                       o_icb.len          = short_ad->len;
+                       o_icb.loc.part_num = udf_rw16(0);       /* ignore */
+                       o_icb.loc.lb_num   = short_ad->lb_num;
+               } else if (addr_type == UDF_ICB_LONG_ALLOC) {
+                       long_ad = (struct long_ad *) (data_pos + offset);
+                       o_icb = *long_ad;
+               } else {
+                       panic("Invalid address type in udf_append_adslot\n");
+               }
+
+               len = udf_rw32(o_icb.len);
+               if (UDF_EXT_FLAGS(len) == UDF_EXT_ALLOCATED) {
+                       /* adjust counts */
+                       len = UDF_EXT_LEN(len);
+                       logblks_rec -= (len + lb_size -1) / lb_size;
+               }
+       }
+
+       /* check if we're not appending a redirection */
+       flags = UDF_EXT_FLAGS(udf_rw32(icb->len));
+       KASSERT(flags != UDF_EXT_REDIRECT);
+
+       /* round down available space */
+       rest = adlen * ((max_l_ad - offset) / adlen);
+       if (rest <= adlen) {
+               /* have to append aed, see if we already have a spare one */
+               extnr++;
+               ext = udf_node->ext[extnr];
+               l_icb = udf_node->ext_loc[extnr];
+               if (ext == NULL) {
+                       DPRINTF(ALLOC,("adding allocation extent %d\n", extnr));
+
+                       error = udf_reserve_space(ump, NULL, UDF_C_NODE,
+                                       vpart_num, 1, /* can fail */ false);
+                       if (error) {
+                               printf("UDF: couldn't reserve space for AED!\n");
+                               return error;
+                       }
+                       error = udf_allocate_space(ump, NULL, UDF_C_NODE,
+                                       vpart_num, 1, &lmapping);
+                       lb_num = lmapping;
+                       if (error)
+                               panic("UDF: couldn't allocate AED!\n");
+
+                       /* initialise pointer to location */
+                       memset(&l_icb, 0, sizeof(struct long_ad));
+                       l_icb.len = udf_rw32(lb_size | UDF_EXT_REDIRECT);
+                       l_icb.loc.lb_num   = udf_rw32(lb_num);
+                       l_icb.loc.part_num = udf_rw16(vpart_num);
+
+                       /* create new aed descriptor */
+                       udf_create_logvol_dscr(ump, udf_node, &l_icb, &extdscr);
+                       ext = &extdscr->aee;
+
+                       udf_inittag(ump, &ext->tag, TAGID_ALLOCEXTENT, lb_num);
+                       dscr_size  = sizeof(struct alloc_ext_entry) -1;
+                       max_l_ad = lb_size - dscr_size;
+                       memset(ext->data, 0, max_l_ad);
+                       ext->l_ad = udf_rw32(0);
+                       ext->tag.desc_crc_len =
+                               udf_rw16(dscr_size - UDF_DESC_TAG_LENGTH);
+
+                       /* declare aed */
+                       udf_node->num_extensions++;
+                       udf_node->ext_loc[extnr] = l_icb;
+                       udf_node->ext[extnr] = ext;
+               }
+               /* add redirect and adjust l_ad and crclen for old descr */
+               if (addr_type == UDF_ICB_SHORT_ALLOC) {
+                       short_ad = (struct short_ad *) (data_pos + offset);
+                       short_ad->len    = l_icb.len;
+                       short_ad->lb_num = l_icb.loc.lb_num;
+               } else if (addr_type == UDF_ICB_LONG_ALLOC) {
+                       long_ad = (struct long_ad *) (data_pos + offset);
+                       *long_ad = l_icb;
+               }
+               l_ad   += adlen;
+               crclen += adlen;
+               dscr->tag.desc_crc_len = udf_rw16(crclen);
+               *l_ad_p = udf_rw32(l_ad);
+
+               /* advance to the new extension */
+               KASSERT(ext != NULL);
+               dscr = (union dscrptr *) ext;
+               dscr_size  = sizeof(struct alloc_ext_entry) -1;
+               max_l_ad = lb_size - dscr_size;
+               data_pos = (uint8_t *) dscr + dscr_size;
+
+               l_ad_p = &ext->l_ad;
+               l_ad   = udf_rw32(*l_ad_p);
+               crclen = udf_rw16(dscr->tag.desc_crc_len);
+               offset = 0;
+
+               /* adjust callees slot count for link insert */
+               *slot += 1;
+       }
+
+       /* write out the element */
+       DPRINTF(PARANOIDADWLK, ("adding element : %p : v %d, lb %d, "
+                       "len %d, flags %d\n", data_pos + offset,
+                       icb->loc.part_num, icb->loc.lb_num,
+                       UDF_EXT_LEN(icb->len), UDF_EXT_FLAGS(icb->len)));
+       if (addr_type == UDF_ICB_SHORT_ALLOC) {
+               short_ad = (struct short_ad *) (data_pos + offset);
+               short_ad->len    = icb->len;
+               short_ad->lb_num = icb->loc.lb_num;
+       } else if (addr_type == UDF_ICB_LONG_ALLOC) {
+               long_ad = (struct long_ad *) (data_pos + offset);
+               *long_ad = *icb;
+       }
+
+       /* adjust logblks recorded count */
+       len = udf_rw32(icb->len);
+       flags = UDF_EXT_FLAGS(len);
+       if (flags == UDF_EXT_ALLOCATED)
+               logblks_rec += (UDF_EXT_LEN(len) + lb_size -1) / lb_size;
+       *logblks_rec_p = udf_rw64(logblks_rec);
+
+       /* adjust l_ad and crclen when needed */
+       if (offset >= l_ad) {
+               l_ad   += adlen;
+               crclen += adlen;
+               dscr->tag.desc_crc_len = udf_rw16(crclen);
+               *l_ad_p = udf_rw32(l_ad);
+       }
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void
+udf_count_alloc_exts(struct udf_node *udf_node)
+{
+       struct long_ad s_ad;
+       uint32_t lb_num, len, flags;
+       uint16_t vpart_num;
+       int slot, eof;
+       int num_extents, extnr;
+
+       if (udf_node->num_extensions == 0)
+               return;
+
+       /* count number of allocation extents in use */
+       num_extents = 0;
+       slot = 0;
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &s_ad, &eof);
+               if (eof)
+                       break;
+               len   = udf_rw32(s_ad.len);
+               flags = UDF_EXT_FLAGS(len);
+
+               if (flags == UDF_EXT_REDIRECT)
+                       num_extents++;
+
+               slot++;
+       }
+
+       DPRINTF(ALLOC, ("udf_count_alloc_ext counted %d live extents\n",
+               num_extents));
+
+       /* XXX choice: we could delay freeing them on node writeout */
+       /* free excess entries */
+       extnr = num_extents;
+       for (;extnr < udf_node->num_extensions; extnr++) {
+               DPRINTF(ALLOC, ("freeing alloc ext %d\n", extnr));
+               /* free dscriptor */
+               s_ad = udf_node->ext_loc[extnr];
+               udf_free_logvol_dscr(udf_node->ump, &s_ad,
+                       udf_node->ext[extnr]);
+               udf_node->ext[extnr] = NULL;
+
+               /* free disc space */
+               lb_num    = udf_rw32(s_ad.loc.lb_num);
+               vpart_num = udf_rw16(s_ad.loc.part_num);
+               udf_free_allocated_space(udf_node->ump, lb_num, vpart_num, 1);
+
+               memset(&udf_node->ext_loc[extnr], 0, sizeof(struct long_ad));
+       }
+
+       /* set our new number of allocation extents */
+       udf_node->num_extensions = num_extents;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Adjust the node's allocation descriptors to reflect the new mapping; do
+ * take note that we might glue to existing allocation descriptors.
+ *
+ * XXX Note there can only be one allocation being recorded/mount; maybe
+ * explicit allocation in shedule thread?
+ */
+
+static void
+udf_record_allocation_in_node(struct udf_mount *ump, struct buf *buf,
+       uint16_t vpart_num, uint64_t *mapping, struct long_ad *node_ad_cpy)
+{
+       struct vnode    *vp = buf->b_vp;
+       struct udf_node *udf_node = VTOI(vp);
+       struct file_entry      *fe;
+       struct extfile_entry   *efe;
+       struct icb_tag  *icbtag;
+       struct long_ad   s_ad, c_ad;
+       uint64_t inflen, from, till;
+       uint64_t foffset, end_foffset, restart_foffset;
+       uint64_t orig_inflen, orig_lbrec, new_inflen, new_lbrec;
+       uint32_t max_len;
+       uint32_t num_lb, len, flags, lb_num;
+       uint32_t run_start;
+       uint32_t slot_offset, replace_len, replace;
+       int addr_type, icbflags;
+//     int udf_c_type = buf->b_udf_c_type;
+       int lb_size, run_length, eof;
+       int slot, cpy_slot, cpy_slots, restart_slot;
+       int error;
+
+       DPRINTF(ALLOC, ("udf_record_allocation_in_node\n"));
+
+#if 0
+       /* XXX disable sanity check for now */
+       /* sanity check ... should be panic ? */
+       if ((udf_c_type != UDF_C_USERDATA) && (udf_c_type != UDF_C_FIDS))
+               return;
+#endif
+
+       lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
+       max_len = ((UDF_EXT_MAXLEN / lb_size) * lb_size);
+
+       /* do the job */
+       UDF_LOCK_NODE(udf_node, 0);     /* XXX can deadlock ? */
+       udf_node_sanity_check(udf_node, &orig_inflen, &orig_lbrec);
+
+       fe  = udf_node->fe;
+       efe = udf_node->efe;
+       if (fe) {
+               icbtag = &fe->icbtag;
+               inflen = udf_rw64(fe->inf_len);
+       } else {
+               icbtag = &efe->icbtag;
+               inflen = udf_rw64(efe->inf_len);
+       }
+
+       /* do check if `till' is not past file information length */
+       from = buf->b_lblkno * lb_size;
+       till = MIN(inflen, from + buf->b_resid);
+
+       num_lb = (till - from + lb_size -1) / lb_size;
+
+       DPRINTF(ALLOC, ("record allocation from %"PRIu64" + %d\n", from, buf->b_bcount));
+
+       icbflags  = udf_rw16(icbtag->flags);
+       addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       if (addr_type == UDF_ICB_INTERN_ALLOC) {
+               /* nothing to do */
+               /* XXX clean up rest of node? just in case? */
+               UDF_UNLOCK_NODE(udf_node, 0);
+               return;
+       }
+
+       slot     = 0;
+       cpy_slot = 0;
+       foffset  = 0;
+
+       /* 1) copy till first overlap piece to the rewrite buffer */
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &s_ad, &eof);
+               if (eof) {
+                       DPRINTF(WRITE,
+                               ("Record allocation in node "
+                                "failed: encountered EOF\n"));
+                       UDF_UNLOCK_NODE(udf_node, 0);
+                       buf->b_error = EINVAL;
+                       return;
+               }
+               len   = udf_rw32(s_ad.len);
+               flags = UDF_EXT_FLAGS(len);
+               len   = UDF_EXT_LEN(len);
+
+               if (flags == UDF_EXT_REDIRECT) {
+                       slot++;
+                       continue;
+               }
+
+               end_foffset = foffset + len;
+               if (end_foffset > from)
+                       break;  /* found */
+
+               node_ad_cpy[cpy_slot++] = s_ad;
+
+               DPRINTF(ALLOC, ("\t1: vp %d, lb %d, len %d, flags %d "
+                       "-> stack\n",
+                       udf_rw16(s_ad.loc.part_num),
+                       udf_rw32(s_ad.loc.lb_num),
+                       UDF_EXT_LEN(udf_rw32(s_ad.len)),
+                       UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+
+               foffset = end_foffset;
+               slot++;
+       }
+       restart_slot    = slot;
+       restart_foffset = foffset;
+
+       /* 2) trunc overlapping slot at overlap and copy it */
+       slot_offset = from - foffset;
+       if (slot_offset > 0) {
+               DPRINTF(ALLOC, ("\tslot_offset = %d, flags = %d (%d)\n",
+                               slot_offset, flags >> 30, flags));
+
+               s_ad.len = udf_rw32(slot_offset | flags);
+               node_ad_cpy[cpy_slot++] = s_ad;
+
+               DPRINTF(ALLOC, ("\t2: vp %d, lb %d, len %d, flags %d "
+                       "-> stack\n",
+                       udf_rw16(s_ad.loc.part_num),
+                       udf_rw32(s_ad.loc.lb_num),
+                       UDF_EXT_LEN(udf_rw32(s_ad.len)),
+                       UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+       }
+       foffset += slot_offset;
+
+       /* 3) insert new mappings */
+       memset(&s_ad, 0, sizeof(struct long_ad));
+       lb_num = 0;
+       for (lb_num = 0; lb_num < num_lb; lb_num++) {
+               run_start  = mapping[lb_num];
+               run_length = 1;
+               while (lb_num < num_lb-1) {
+                       if (mapping[lb_num+1] != mapping[lb_num]+1)
+                               if (mapping[lb_num+1] != mapping[lb_num])
+                                       break;
+                       run_length++;
+                       lb_num++;
+               }
+               /* insert slot for this mapping */
+               len = run_length * lb_size;
+
+               /* bounds checking */
+               if (foffset + len > till)
+                       len = till - foffset;
+               KASSERT(foffset + len <= inflen);
+
+               s_ad.len = udf_rw32(len | UDF_EXT_ALLOCATED);
+               s_ad.loc.part_num = udf_rw16(vpart_num);
+               s_ad.loc.lb_num   = udf_rw32(run_start);
+
+               foffset += len;
+
+               /* paranoia */
+               if (len == 0) {
+                       DPRINTF(WRITE,
+                               ("Record allocation in node "
+                                "failed: insert failed\n"));
+                       UDF_UNLOCK_NODE(udf_node, 0);
+                       buf->b_error = EINVAL;
+                       return;
+               }
+               node_ad_cpy[cpy_slot++] = s_ad;
+
+               DPRINTF(ALLOC, ("\t3: insert new mapping vp %d lb %d, len %d, "
+                               "flags %d -> stack\n",
+                       udf_rw16(s_ad.loc.part_num), udf_rw32(s_ad.loc.lb_num),
+                       UDF_EXT_LEN(udf_rw32(s_ad.len)),
+                       UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+       }
+
+       /* 4) pop replaced length */
+       slot    = restart_slot;
+       foffset = restart_foffset;
+
+       replace_len = till - foffset;   /* total amount of bytes to pop */
+       slot_offset = from - foffset;   /* offset in first encounted slot */
+       KASSERT((slot_offset % lb_size) == 0);
+
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &s_ad, &eof);
+               if (eof)
+                       break;
+
+               len    = udf_rw32(s_ad.len);
+               flags  = UDF_EXT_FLAGS(len);
+               len    = UDF_EXT_LEN(len);
+               lb_num = udf_rw32(s_ad.loc.lb_num);
+
+               if (flags == UDF_EXT_REDIRECT) {
+                       slot++;
+                       continue;
+               }
+
+               DPRINTF(ALLOC, ("\t4i: got slot %d, slot_offset %d, "
+                               "replace_len %d, "
+                               "vp %d, lb %d, len %d, flags %d\n",
+                       slot, slot_offset, replace_len,
+                       udf_rw16(s_ad.loc.part_num),
+                       udf_rw32(s_ad.loc.lb_num),
+                       UDF_EXT_LEN(udf_rw32(s_ad.len)),
+                       UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+
+               /* adjust for slot offset */
+               if (slot_offset) {
+                       DPRINTF(ALLOC, ("\t4s: skipping %d\n", slot_offset));
+                       lb_num += slot_offset / lb_size;
+                       len    -= slot_offset;
+                       foffset += slot_offset;
+                       replace_len -= slot_offset;
+
+                       /* mark adjusted */
+                       slot_offset = 0;
+               }
+
+               /* advance for (the rest of) this slot */
+               replace = MIN(len, replace_len);
+               DPRINTF(ALLOC, ("\t4d: replacing %d\n", replace));
+
+               /* advance for this slot */
+               if (replace) {
+                       /* note: dont round DOWN on num_lb since we then
+                        * forget the last partial one */
+                       num_lb = (replace + lb_size - 1) / lb_size;
+                       if (flags != UDF_EXT_FREE) {
+                               udf_free_allocated_space(ump, lb_num,
+                                       udf_rw16(s_ad.loc.part_num), num_lb);
+                       }
+                       lb_num      += num_lb;
+                       len         -= replace;
+                       foffset     += replace;
+                       replace_len -= replace;
+               }
+
+               /* do we have a slot tail ? */
+               if (len) {
+                       KASSERT(foffset % lb_size == 0);
+
+                       /* we arrived at our point, push remainder */
+                       s_ad.len        = udf_rw32(len | flags);
+                       s_ad.loc.lb_num = udf_rw32(lb_num);
+                       if (flags == UDF_EXT_FREE)
+                               s_ad.loc.lb_num = udf_rw32(0);
+                       node_ad_cpy[cpy_slot++] = s_ad;
+                       foffset += len;
+                       slot++;
+
+                       DPRINTF(ALLOC, ("\t4: vp %d, lb %d, len %d, flags %d "
+                               "-> stack\n",
+                               udf_rw16(s_ad.loc.part_num),
+                               udf_rw32(s_ad.loc.lb_num),
+                               UDF_EXT_LEN(udf_rw32(s_ad.len)),
+                               UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+                       break;
+               }
+
+               slot++;
+       }
+
+       /* 5) copy remainder */
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &s_ad, &eof);
+               if (eof)
+                       break;
+
+               len   = udf_rw32(s_ad.len);
+               flags = UDF_EXT_FLAGS(len);
+               len   = UDF_EXT_LEN(len);
+
+               if (flags == UDF_EXT_REDIRECT) {
+                       slot++;
+                       continue;
+               }
+
+               node_ad_cpy[cpy_slot++] = s_ad;
+
+               DPRINTF(ALLOC, ("\t5: insert new mapping "
+                       "vp %d lb %d, len %d, flags %d "
+                       "-> stack\n",
+               udf_rw16(s_ad.loc.part_num),
+               udf_rw32(s_ad.loc.lb_num),
+               UDF_EXT_LEN(udf_rw32(s_ad.len)),
+               UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+
+               slot++;
+       }
+
+       /* 6) reset node descriptors */
+       udf_wipe_adslots(udf_node);
+
+       /* 7) copy back extents; merge when possible. Recounting on the fly */
+       cpy_slots = cpy_slot;
+
+       c_ad = node_ad_cpy[0];
+       slot = 0;
+       DPRINTF(ALLOC, ("\t7s: stack -> got mapping vp %d "
+               "lb %d, len %d, flags %d\n",
+       udf_rw16(c_ad.loc.part_num),
+       udf_rw32(c_ad.loc.lb_num),
+       UDF_EXT_LEN(udf_rw32(c_ad.len)),
+       UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30));
+
+       for (cpy_slot = 1; cpy_slot < cpy_slots; cpy_slot++) {
+               s_ad = node_ad_cpy[cpy_slot];
+
+               DPRINTF(ALLOC, ("\t7i: stack -> got mapping vp %d "
+                       "lb %d, len %d, flags %d\n",
+               udf_rw16(s_ad.loc.part_num),
+               udf_rw32(s_ad.loc.lb_num),
+               UDF_EXT_LEN(udf_rw32(s_ad.len)),
+               UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+
+               /* see if we can merge */
+               if (udf_ads_merge(max_len, lb_size, &c_ad, &s_ad)) {
+                       /* not mergable (anymore) */
+                       DPRINTF(ALLOC, ("\t7: appending vp %d lb %d, "
+                               "len %d, flags %d\n",
+                       udf_rw16(c_ad.loc.part_num),
+                       udf_rw32(c_ad.loc.lb_num),
+                       UDF_EXT_LEN(udf_rw32(c_ad.len)),
+                       UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30));
+
+                       error = udf_append_adslot(udf_node, &slot, &c_ad);
+                       if (error) {
+                               buf->b_error = error;
+                               goto out;
+                       }
+                       c_ad = s_ad;
+                       slot++;
+               }
+       }
+
+       /* 8) push rest slot (if any) */
+       if (UDF_EXT_LEN(c_ad.len) > 0) {
+               DPRINTF(ALLOC, ("\t8: last append vp %d lb %d, "
+                               "len %d, flags %d\n",
+               udf_rw16(c_ad.loc.part_num),
+               udf_rw32(c_ad.loc.lb_num),
+               UDF_EXT_LEN(udf_rw32(c_ad.len)),
+               UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30));
+
+               error = udf_append_adslot(udf_node, &slot, &c_ad);
+               if (error) {
+                       buf->b_error = error;
+                       goto out;
+               }
+       }
+
+out:
+       udf_count_alloc_exts(udf_node);
+
+       /* the node's descriptors should now be sane */
+       udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec);
+       UDF_UNLOCK_NODE(udf_node, 0);
+
+       KASSERT(orig_inflen == new_inflen);
+       KASSERT(new_lbrec >= orig_lbrec);
+
+       return;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_grow_node(struct udf_node *udf_node, uint64_t new_size)
+{
+       struct vnode *vp = udf_node->vnode;
+       struct udf_mount *ump = udf_node->ump;
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       struct icb_tag  *icbtag;
+       struct long_ad c_ad, s_ad;
+       uint64_t size_diff, old_size, inflen, objsize, chunk, append_len;
+       uint64_t foffset, end_foffset;
+       uint64_t orig_inflen, orig_lbrec, new_inflen, new_lbrec;
+       uint32_t lb_size, unit_size, dscr_size, crclen, lastblock_grow;
+       uint32_t icbflags, len, flags, max_len;
+       uint32_t max_l_ad, l_ad, l_ea;
+       uint16_t my_part, dst_part;
+       uint8_t *evacuated_data;
+       int addr_type;
+       int slot;
+       int eof, error;
+
+       DPRINTF(ALLOC, ("udf_grow_node\n"));
+
+       UDF_LOCK_NODE(udf_node, 0);
+       udf_node_sanity_check(udf_node, &orig_inflen, &orig_lbrec);
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+
+       /* max_len in unit's IFF its a metadata node or metadata mirror node */
+       unit_size = lb_size;
+       if ((udf_node == ump->metadata_node) || (udf_node == ump->metadatamirror_node))
+               unit_size = ump->metadata_alloc_unit_size * lb_size;
+       max_len = ((UDF_EXT_MAXLEN / unit_size) * unit_size);
+
+       fe  = udf_node->fe;
+       efe = udf_node->efe;
+       if (fe) {
+               icbtag  = &fe->icbtag;
+               inflen  = udf_rw64(fe->inf_len);
+               objsize = inflen;
+               dscr_size  = sizeof(struct file_entry) -1;
+               l_ea       = udf_rw32(fe->l_ea);
+               l_ad       = udf_rw32(fe->l_ad);
+       } else {
+               icbtag  = &efe->icbtag;
+               inflen  = udf_rw64(efe->inf_len);
+               objsize = udf_rw64(efe->obj_size);
+               dscr_size  = sizeof(struct extfile_entry) -1;
+               l_ea       = udf_rw32(efe->l_ea);
+               l_ad       = udf_rw32(efe->l_ad);
+       }
+       max_l_ad = lb_size - dscr_size - l_ea;
+
+       icbflags   = udf_rw16(icbtag->flags);
+       addr_type  = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       old_size  = inflen;
+       size_diff = new_size - old_size;
+
+       DPRINTF(ALLOC, ("\tfrom %"PRIu64" to %"PRIu64"\n", old_size, new_size));
+
+       evacuated_data = NULL;
+       if (addr_type == UDF_ICB_INTERN_ALLOC) {
+               if (l_ad + size_diff <= max_l_ad) {
+                       /* only reflect size change directly in the node */
+                       inflen  += size_diff;
+                       objsize += size_diff;
+                       l_ad    += size_diff;
+                       crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea + l_ad;
+                       if (fe) {
+                               fe->inf_len   = udf_rw64(inflen);
+                               fe->l_ad      = udf_rw32(l_ad);
+                               fe->tag.desc_crc_len = udf_rw16(crclen);
+                       } else {
+                               efe->inf_len  = udf_rw64(inflen);
+                               efe->obj_size = udf_rw64(objsize);
+                               efe->l_ad     = udf_rw32(l_ad);
+                               efe->tag.desc_crc_len = udf_rw16(crclen);
+                       }
+                       error = 0;
+
+                       /* set new size for uvm */
+                       uvm_vnp_setwritesize(vp, new_size);
+                       uvm_vnp_setsize(vp, new_size);
+
+#if 0
+                       /* zero append space in buffer */
+                       ubc_zerorange(&vp->v_uobj, old_size,
+                           new_size - old_size, UBC_UNMAP_FLAG(vp));
+#endif
+       
+                       udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec);
+
+                       /* unlock */
+                       UDF_UNLOCK_NODE(udf_node, 0);
+
+                       KASSERT(new_inflen == orig_inflen + size_diff);
+                       KASSERT(new_lbrec == orig_lbrec);
+                       KASSERT(new_lbrec == 0);
+                       return 0;
+               }
+
+               DPRINTF(ALLOC, ("\tCONVERT from internal\n"));
+
+               if (old_size > 0) {
+                       /* allocate some space and copy in the stuff to keep */
+                       evacuated_data = malloc(lb_size, M_UDFTEMP, M_WAITOK);
+                       memset(evacuated_data, 0, lb_size);
+
+                       /* node is locked, so safe to exit mutex */
+                       UDF_UNLOCK_NODE(udf_node, 0);
+
+                       /* read in using the `normal' vn_rdwr() */
+                       error = vn_rdwr(UIO_READ, udf_node->vnode,
+                                       evacuated_data, old_size, 0, 
+                                       UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED,
+                                       FSCRED, NULL, NULL);
+
+                       /* enter again */
+                       UDF_LOCK_NODE(udf_node, 0);
+               }
+
+               /* convert to a normal alloc and select type */
+               my_part  = udf_rw16(udf_node->loc.loc.part_num);
+               dst_part = udf_get_record_vpart(ump, udf_get_c_type(udf_node));
+               addr_type = UDF_ICB_SHORT_ALLOC;
+               if (dst_part != my_part)
+                       addr_type = UDF_ICB_LONG_ALLOC;
+
+               icbflags &= ~UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+               icbflags |= addr_type;
+               icbtag->flags = udf_rw16(icbflags);
+
+               /* wipe old descriptor space */
+               udf_wipe_adslots(udf_node);
+
+               memset(&c_ad, 0, sizeof(struct long_ad));
+               c_ad.len          = udf_rw32(old_size | UDF_EXT_FREE);
+               c_ad.loc.part_num = udf_rw16(0); /* not relevant */
+               c_ad.loc.lb_num   = udf_rw32(0); /* not relevant */
+
+               slot = 0;
+       } else {
+               /* goto the last entry (if any) */
+               slot     = 0;
+               foffset  = 0;
+               memset(&c_ad, 0, sizeof(struct long_ad));
+               for (;;) {
+                       udf_get_adslot(udf_node, slot, &c_ad, &eof);
+                       if (eof)
+                               break;
+
+                       len   = udf_rw32(c_ad.len);
+                       flags = UDF_EXT_FLAGS(len);
+                       len   = UDF_EXT_LEN(len);
+
+                       end_foffset = foffset + len;
+                       if (flags != UDF_EXT_REDIRECT)
+                               foffset = end_foffset;
+
+                       slot++;
+               }
+               /* at end of adslots */
+
+               /* special case if the old size was zero, then there is no last slot */
+               if (old_size == 0) {
+                       c_ad.len          = udf_rw32(0 | UDF_EXT_FREE);
+                       c_ad.loc.part_num = udf_rw16(0); /* not relevant */
+                       c_ad.loc.lb_num   = udf_rw32(0); /* not relevant */
+               } else {
+                       /* refetch last slot */
+                       slot--;
+                       udf_get_adslot(udf_node, slot, &c_ad, &eof);
+               }
+       }
+
+       /*
+        * If the length of the last slot is not a multiple of lb_size, adjust
+        * length so that it is; don't forget to adjust `append_len'! relevant for
+        * extending existing files
+        */
+       len   = udf_rw32(c_ad.len);
+       flags = UDF_EXT_FLAGS(len);
+       len   = UDF_EXT_LEN(len);
+
+       lastblock_grow = 0;
+       if (len % lb_size > 0) {
+               lastblock_grow = lb_size - (len % lb_size);
+               lastblock_grow = MIN(size_diff, lastblock_grow);
+               len += lastblock_grow;
+               c_ad.len = udf_rw32(len | flags);
+
+               /* TODO zero appened space in buffer! */
+               /* using ubc_zerorange(&vp->v_uobj, old_size, */
+               /*    new_size - old_size, UBC_UNMAP_FLAG(vp)); ? */
+       }
+       memset(&s_ad, 0, sizeof(struct long_ad));
+
+       /* size_diff can be bigger than allowed, so grow in chunks */
+       append_len = size_diff - lastblock_grow;
+       while (append_len > 0) {
+               chunk = MIN(append_len, max_len);
+               s_ad.len = udf_rw32(chunk | UDF_EXT_FREE);
+               s_ad.loc.part_num = udf_rw16(0);
+               s_ad.loc.lb_num   = udf_rw32(0);
+
+               if (udf_ads_merge(max_len, lb_size, &c_ad, &s_ad)) {
+                       /* not mergable (anymore) */
+                       error = udf_append_adslot(udf_node, &slot, &c_ad);
+                       if (error)
+                               goto errorout;
+                       slot++;
+                       c_ad = s_ad;
+                       memset(&s_ad, 0, sizeof(struct long_ad));
+               }
+               append_len -= chunk;
+       }
+
+       /* if there is a rest piece in the accumulator, append it */
+       if (UDF_EXT_LEN(udf_rw32(c_ad.len)) > 0) {
+               error = udf_append_adslot(udf_node, &slot, &c_ad);
+               if (error)
+                       goto errorout;
+               slot++;
+       }
+
+       /* if there is a rest piece that didn't fit, append it */
+       if (UDF_EXT_LEN(udf_rw32(s_ad.len)) > 0) {
+               error = udf_append_adslot(udf_node, &slot, &s_ad);
+               if (error)
+                       goto errorout;
+               slot++;
+       }
+
+       inflen  += size_diff;
+       objsize += size_diff;
+       if (fe) {
+               fe->inf_len   = udf_rw64(inflen);
+       } else {
+               efe->inf_len  = udf_rw64(inflen);
+               efe->obj_size = udf_rw64(objsize);
+       }
+       error = 0;
+
+       if (evacuated_data) {
+               /* set new write size for uvm */
+               uvm_vnp_setwritesize(vp, old_size);
+
+               /* write out evacuated data */
+               error = vn_rdwr(UIO_WRITE, udf_node->vnode,
+                               evacuated_data, old_size, 0, 
+                               UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED,
+                               FSCRED, NULL, NULL);
+               uvm_vnp_setsize(vp, old_size);
+       }
+
+errorout:
+       if (evacuated_data)
+               free(evacuated_data, M_UDFTEMP);
+
+       udf_count_alloc_exts(udf_node);
+
+       udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec);
+       UDF_UNLOCK_NODE(udf_node, 0);
+
+       KASSERT(new_inflen == orig_inflen + size_diff);
+       KASSERT(new_lbrec == orig_lbrec);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_shrink_node(struct udf_node *udf_node, uint64_t new_size)
+{
+       struct vnode *vp = udf_node->vnode;
+       struct udf_mount *ump = udf_node->ump;
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       struct icb_tag  *icbtag;
+       struct long_ad c_ad, s_ad, *node_ad_cpy;
+       uint64_t size_diff, old_size, inflen, objsize;
+       uint64_t foffset, end_foffset;
+       uint64_t orig_inflen, orig_lbrec, new_inflen, new_lbrec;
+       uint32_t lb_size, unit_size, dscr_size, crclen;
+       uint32_t slot_offset, slot_offset_lb;
+       uint32_t len, flags, max_len;
+       uint32_t num_lb, lb_num;
+       uint32_t max_l_ad, l_ad, l_ea;
+       uint16_t vpart_num;
+       uint8_t *data_pos;
+       int icbflags, addr_type;
+       int slot, cpy_slot, cpy_slots;
+       int eof, error;
+
+       DPRINTF(ALLOC, ("udf_shrink_node\n"));
+
+       UDF_LOCK_NODE(udf_node, 0);
+       udf_node_sanity_check(udf_node, &orig_inflen, &orig_lbrec);
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+
+       /* max_len in unit's IFF its a metadata node or metadata mirror node */
+       unit_size = lb_size;
+       if ((udf_node == ump->metadata_node) || (udf_node == ump->metadatamirror_node))
+               unit_size = ump->metadata_alloc_unit_size * lb_size;
+       max_len = ((UDF_EXT_MAXLEN / unit_size) * unit_size);
+
+       /* do the work */
+       fe  = udf_node->fe;
+       efe = udf_node->efe;
+       if (fe) {
+               icbtag  = &fe->icbtag;
+               inflen  = udf_rw64(fe->inf_len);
+               objsize = inflen;
+               dscr_size  = sizeof(struct file_entry) -1;
+               l_ea       = udf_rw32(fe->l_ea);
+               l_ad       = udf_rw32(fe->l_ad);
+               data_pos = (uint8_t *) fe + dscr_size + l_ea;
+       } else {
+               icbtag  = &efe->icbtag;
+               inflen  = udf_rw64(efe->inf_len);
+               objsize = udf_rw64(efe->obj_size);
+               dscr_size  = sizeof(struct extfile_entry) -1;
+               l_ea       = udf_rw32(efe->l_ea);
+               l_ad       = udf_rw32(efe->l_ad);
+               data_pos = (uint8_t *) efe + dscr_size + l_ea;
+       }
+       max_l_ad = lb_size - dscr_size - l_ea;
+
+       icbflags   = udf_rw16(icbtag->flags);
+       addr_type  = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       old_size  = inflen;
+       size_diff = old_size - new_size;
+
+       DPRINTF(ALLOC, ("\tfrom %"PRIu64" to %"PRIu64"\n", old_size, new_size));
+
+       /* shrink the node to its new size */
+       if (addr_type == UDF_ICB_INTERN_ALLOC) {
+               /* only reflect size change directly in the node */
+               KASSERT(new_size <= max_l_ad);
+               inflen  -= size_diff;
+               objsize -= size_diff;
+               l_ad    -= size_diff;
+               crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea + l_ad;
+               if (fe) {
+                       fe->inf_len   = udf_rw64(inflen);
+                       fe->l_ad      = udf_rw32(l_ad);
+                       fe->tag.desc_crc_len = udf_rw16(crclen);
+               } else {
+                       efe->inf_len  = udf_rw64(inflen);
+                       efe->obj_size = udf_rw64(objsize);
+                       efe->l_ad     = udf_rw32(l_ad);
+                       efe->tag.desc_crc_len = udf_rw16(crclen);
+               }
+               error = 0;
+
+               /* clear the space in the descriptor */
+               KASSERT(old_size > new_size);
+               memset(data_pos + new_size, 0, old_size - new_size);
+
+               /* TODO zero appened space in buffer! */
+               /* using ubc_zerorange(&vp->v_uobj, old_size, */
+               /*    old_size - new_size, UBC_UNMAP_FLAG(vp)); ? */
+
+               /* set new size for uvm */
+               uvm_vnp_setsize(vp, new_size);
+
+               udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec);
+               UDF_UNLOCK_NODE(udf_node, 0);
+
+               KASSERT(new_inflen == orig_inflen - size_diff);
+               KASSERT(new_lbrec == orig_lbrec);
+               KASSERT(new_lbrec == 0);
+
+               return 0;
+       }
+
+       /* setup node cleanup extents copy space */
+       node_ad_cpy = malloc(lb_size * UDF_MAX_ALLOC_EXTENTS,
+               M_UDFMNT, M_WAITOK);
+       memset(node_ad_cpy, 0, lb_size * UDF_MAX_ALLOC_EXTENTS);
+
+       /*
+        * Shrink the node by releasing the allocations and truncate the last
+        * allocation to the new size. If the new size fits into the
+        * allocation descriptor itself, transform it into an
+        * UDF_ICB_INTERN_ALLOC.
+        */
+       slot     = 0;
+       cpy_slot = 0;
+       foffset  = 0;
+
+       /* 1) copy till first overlap piece to the rewrite buffer */
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &s_ad, &eof);
+               if (eof) {
+                       DPRINTF(WRITE,
+                               ("Shrink node failed: "
+                                "encountered EOF\n"));
+                       error = EINVAL;
+                       goto errorout; /* panic? */
+               }
+               len   = udf_rw32(s_ad.len);
+               flags = UDF_EXT_FLAGS(len);
+               len   = UDF_EXT_LEN(len);
+
+               if (flags == UDF_EXT_REDIRECT) {
+                       slot++;
+                       continue;
+               }
+
+               end_foffset = foffset + len;
+               if (end_foffset > new_size)
+                       break;  /* found */
+
+               node_ad_cpy[cpy_slot++] = s_ad;
+
+               DPRINTF(ALLOC, ("\t1: vp %d, lb %d, len %d, flags %d "
+                       "-> stack\n",
+                       udf_rw16(s_ad.loc.part_num),
+                       udf_rw32(s_ad.loc.lb_num),
+                       UDF_EXT_LEN(udf_rw32(s_ad.len)),
+                       UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+
+               foffset = end_foffset;
+               slot++;
+       }
+       slot_offset = new_size - foffset;
+
+       /* 2) trunc overlapping slot at overlap and copy it */
+       if (slot_offset > 0) {
+               lb_num    = udf_rw32(s_ad.loc.lb_num);
+               vpart_num = udf_rw16(s_ad.loc.part_num);
+
+               if (flags == UDF_EXT_ALLOCATED) {
+                       /* calculate extent in lb, and offset in lb */
+                       num_lb = (len + lb_size -1) / lb_size;
+                       slot_offset_lb = (slot_offset + lb_size -1) / lb_size;
+
+                       /* adjust our slot */
+                       lb_num += slot_offset_lb;
+                       num_lb -= slot_offset_lb;
+
+                       udf_free_allocated_space(ump, lb_num, vpart_num, num_lb);
+               }
+
+               s_ad.len = udf_rw32(slot_offset | flags);
+               node_ad_cpy[cpy_slot++] = s_ad;
+               slot++;
+
+               DPRINTF(ALLOC, ("\t2: vp %d, lb %d, len %d, flags %d "
+                       "-> stack\n",
+                       udf_rw16(s_ad.loc.part_num),
+                       udf_rw32(s_ad.loc.lb_num),
+                       UDF_EXT_LEN(udf_rw32(s_ad.len)),
+                       UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+       }
+
+       /* 3) delete remainder */
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &s_ad, &eof);
+               if (eof)
+                       break;
+
+               len       = udf_rw32(s_ad.len);
+               flags     = UDF_EXT_FLAGS(len);
+               len       = UDF_EXT_LEN(len);
+
+               if (flags == UDF_EXT_REDIRECT) {
+                       slot++;
+                       continue;
+               }
+
+               DPRINTF(ALLOC, ("\t3: delete remainder "
+                       "vp %d lb %d, len %d, flags %d\n",
+               udf_rw16(s_ad.loc.part_num),
+               udf_rw32(s_ad.loc.lb_num),
+               UDF_EXT_LEN(udf_rw32(s_ad.len)),
+               UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+
+               if (flags == UDF_EXT_ALLOCATED) {
+                       lb_num    = udf_rw32(s_ad.loc.lb_num);
+                       vpart_num = udf_rw16(s_ad.loc.part_num);
+                       num_lb    = (len + lb_size - 1) / lb_size;
+
+                       udf_free_allocated_space(ump, lb_num, vpart_num,
+                               num_lb);
+               }
+
+               slot++;
+       }
+
+       /* 4) if it will fit into the descriptor then convert */
+       if (new_size < max_l_ad) {
+               /*
+                * resque/evacuate old piece by reading it in, and convert it
+                * to internal alloc.
+                */
+               if (new_size == 0) {
+                       /* XXX/TODO only for zero sizing now */
+                       udf_wipe_adslots(udf_node);
+
+                       icbflags &= ~UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+                       icbflags |=  UDF_ICB_INTERN_ALLOC;
+                       icbtag->flags = udf_rw16(icbflags);
+
+                       inflen  -= size_diff;   KASSERT(inflen == 0);
+                       objsize -= size_diff;
+                       l_ad     = new_size;
+                       crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea + l_ad;
+                       if (fe) {
+                               fe->inf_len   = udf_rw64(inflen);
+                               fe->l_ad      = udf_rw32(l_ad);
+                               fe->tag.desc_crc_len = udf_rw16(crclen);
+                       } else {
+                               efe->inf_len  = udf_rw64(inflen);
+                               efe->obj_size = udf_rw64(objsize);
+                               efe->l_ad     = udf_rw32(l_ad);
+                               efe->tag.desc_crc_len = udf_rw16(crclen);
+                       }
+                       /* eventually copy in evacuated piece */
+                       /* set new size for uvm */
+                       uvm_vnp_setsize(vp, new_size);
+
+                       free(node_ad_cpy, M_UDFMNT);
+                       udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec);
+
+                       UDF_UNLOCK_NODE(udf_node, 0);
+
+                       KASSERT(new_inflen == orig_inflen - size_diff);
+                       KASSERT(new_inflen == 0);
+                       KASSERT(new_lbrec == 0);
+
+                       return 0;
+               }
+
+               printf("UDF_SHRINK_NODE: could convert to internal alloc!\n");
+       }
+
+       /* 5) reset node descriptors */
+       udf_wipe_adslots(udf_node);
+
+       /* 6) copy back extents; merge when possible. Recounting on the fly */
+       cpy_slots = cpy_slot;
+
+       c_ad = node_ad_cpy[0];
+       slot = 0;
+       for (cpy_slot = 1; cpy_slot < cpy_slots; cpy_slot++) {
+               s_ad = node_ad_cpy[cpy_slot];
+
+               DPRINTF(ALLOC, ("\t6: stack -> got mapping vp %d "
+                       "lb %d, len %d, flags %d\n",
+               udf_rw16(s_ad.loc.part_num),
+               udf_rw32(s_ad.loc.lb_num),
+               UDF_EXT_LEN(udf_rw32(s_ad.len)),
+               UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30));
+
+               /* see if we can merge */
+               if (udf_ads_merge(max_len, lb_size, &c_ad, &s_ad)) {
+                       /* not mergable (anymore) */
+                       DPRINTF(ALLOC, ("\t6: appending vp %d lb %d, "
+                               "len %d, flags %d\n",
+                       udf_rw16(c_ad.loc.part_num),
+                       udf_rw32(c_ad.loc.lb_num),
+                       UDF_EXT_LEN(udf_rw32(c_ad.len)),
+                       UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30));
+
+                       error = udf_append_adslot(udf_node, &slot, &c_ad);
+                       if (error)
+                               goto errorout; /* panic? */
+                       c_ad = s_ad;
+                       slot++;
+               }
+       }
+
+       /* 7) push rest slot (if any) */
+       if (UDF_EXT_LEN(c_ad.len) > 0) {
+               DPRINTF(ALLOC, ("\t7: last append vp %d lb %d, "
+                               "len %d, flags %d\n",
+               udf_rw16(c_ad.loc.part_num),
+               udf_rw32(c_ad.loc.lb_num),
+               UDF_EXT_LEN(udf_rw32(c_ad.len)),
+               UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30));
+
+               error = udf_append_adslot(udf_node, &slot, &c_ad);
+               if (error)
+                       goto errorout; /* panic? */
+               ;
+       }
+
+       inflen  -= size_diff;
+       objsize -= size_diff;
+       if (fe) {
+               fe->inf_len   = udf_rw64(inflen);
+       } else {
+               efe->inf_len  = udf_rw64(inflen);
+               efe->obj_size = udf_rw64(objsize);
+       }
+       error = 0;
+
+       /* set new size for uvm */
+       uvm_vnp_setsize(vp, new_size);
+
+errorout:
+       free(node_ad_cpy, M_UDFMNT);
+
+       udf_count_alloc_exts(udf_node);
+
+       udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec);
+       UDF_UNLOCK_NODE(udf_node, 0);
+
+       KASSERT(new_inflen == orig_inflen - size_diff);
+
+       return error;
+}
+
diff --git a/sys/fs/udf/udf_bswap.h b/sys/fs/udf/udf_bswap.h
new file mode 100644 (file)
index 0000000..9a481cf
--- /dev/null
@@ -0,0 +1,76 @@
+/* $NetBSD: udf_bswap.h,v 1.8 2009/10/22 21:50:01 bouyer Exp $ */
+
+/*
+ * Copyright (c) 1998 Manuel Bouyer.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * adapted for UDF by Reinoud Zandijk <reinoud@netbsd.org>
+ *
+ */
+
+#ifndef _FS_UDF_UDF_BSWAP_H_
+#define _FS_UDF_UDF_BSWAP_H_
+
+#include <sys/endian.h>
+#include <machine/bswap.h>
+#include <sys/bswap.h>
+
+/* rest only relevant for big endian machines */
+#if (BYTE_ORDER == BIG_ENDIAN)
+
+/* inlines for access to swapped data */
+static __inline uint16_t udf_rw16(uint16_t);
+static __inline uint32_t udf_rw32(uint32_t);
+static __inline uint64_t udf_rw64(uint64_t);
+
+
+static __inline uint16_t
+udf_rw16(uint16_t a)
+{
+       return bswap16(a);
+}
+
+
+static __inline uint32_t
+udf_rw32(uint32_t a)
+{
+       return bswap32(a);
+}
+
+
+static __inline uint64_t
+udf_rw64(uint64_t a)
+{
+       return bswap64(a);
+}
+
+#else
+
+#define udf_rw16(a) ((uint16_t)(a))
+#define udf_rw32(a) ((uint32_t)(a))
+#define udf_rw64(a) ((uint64_t)(a))
+
+#endif
+
+
+#endif /* !_FS_UDF_UDF_BSWAP_H_ */
+
diff --git a/sys/fs/udf/udf_mount.h b/sys/fs/udf/udf_mount.h
new file mode 100644 (file)
index 0000000..bf02be7
--- /dev/null
@@ -0,0 +1,70 @@
+/* $NetBSD: udf_mount.h,v 1.3 2006/02/02 15:52:23 reinoud Exp $ */
+
+/*
+ * Copyright (c) 2006 Reinoud Zandijk
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *          This product includes software developed for the
+ *          NetBSD Project.  See http://www.NetBSD.org/ for
+ *          information about NetBSD.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+
+#ifndef _FS_UDF_UDF_MOUNT_H_
+#define _FS_UDF_UDF_MOUNT_H_
+
+/*
+ * Arguments to mount UDF filingsystem.
+ */
+
+#define UDFMNT_VERSION 1
+struct udf_args {
+       uint32_t         version;       /* version of this structure         */
+       char            *fspec;         /* mount specifier                   */
+       int32_t          sessionnr;     /* session specifier, rel of abs     */
+       uint32_t         udfmflags;     /* mount options                     */
+       int32_t          gmtoff;        /* offset from UTC in seconds        */
+
+       uid_t            anon_uid;      /* mapping of anonymous files uid    */
+       gid_t            anon_gid;      /* mapping of anonymous files gid    */
+       uid_t            nobody_uid;    /* nobody:nobody will map to -1:-1   */
+       gid_t            nobody_gid;    /* nobody:nobody will map to -1:-1   */
+
+       uint32_t         sector_size;   /* for mounting dumps/files          */
+
+       /* extendable */
+       uint8_t  reserved[32];
+};
+
+
+/* udf mount options */
+
+#define UDFMNT_CLOSESESSION    0x00000001      /* close session on dismount */
+#define UDFMNT_BITS "\20\1CLOSESESSION"
+
+#endif /* !_FS_UDF_UDF_MOUNT_H_ */
+
diff --git a/sys/fs/udf/udf_osta.c b/sys/fs/udf/udf_osta.c
new file mode 100644 (file)
index 0000000..3b1a282
--- /dev/null
@@ -0,0 +1,514 @@
+/* $NetBSD: udf_osta.c,v 1.10 2013/08/05 17:02:54 joerg Exp $ */
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: udf_osta.c,v 1.10 2013/08/05 17:02:54 joerg Exp $");
+
+/*
+ * Various routines from the OSTA 2.01 specs.  Copyrights are included with
+ * each code segment.  Slight whitespace modifications have been made for
+ * formatting purposes.  Typos/bugs have been fixed.
+ *
+ */
+
+#include "udf_osta.h"
+
+#ifndef _KERNEL
+#include <ctype.h>
+#endif
+
+/*****************************************************************************/
+/***********************************************************************
+ * OSTA compliant Unicode compression, uncompression routines.
+ * Copyright 1995 Micro Design International, Inc.
+ * Written by Jason M. Rinn.
+ * Micro Design International gives permission for the free use of the
+ * following source code.
+ */
+
+/***********************************************************************
+ * Takes an OSTA CS0 compressed unicode name, and converts
+ * it to Unicode.
+ * The Unicode output will be in the byte order
+ * that the local compiler uses for 16-bit values.
+ * NOTE: This routine only performs error checking on the compID.
+ * It is up to the user to ensure that the unicode buffer is large
+ * enough, and that the compressed unicode name is correct.
+ *
+ * RETURN VALUE
+ *
+ * The number of unicode characters which were uncompressed.
+ * A -1 is returned if the compression ID is invalid.
+ */
+int
+udf_UncompressUnicode(
+       int numberOfBytes,      /* (Input) number of bytes read from media. */
+       byte *UDFCompressed,    /* (Input) bytes read from media. */
+       unicode_t *unicode)     /* (Output) uncompressed unicode characters. */
+{
+       unsigned int compID;
+       int returnValue, unicodeIndex, byteIndex;
+
+       /* Use UDFCompressed to store current byte being read. */
+       compID = UDFCompressed[0];
+
+       /* First check for valid compID. */
+       if (compID != 8 && compID != 16) {
+               returnValue = -1;
+       } else {
+               unicodeIndex = 0;
+               byteIndex = 1;
+
+               /* Loop through all the bytes. */
+               while (byteIndex < numberOfBytes) {
+                       if (compID == 16) {
+                               /* Move the first byte to the high bits of the
+                                * unicode char.
+                                */
+                               unicode[unicodeIndex] =
+                                   UDFCompressed[byteIndex++] << 8;
+                       } else {
+                               unicode[unicodeIndex] = 0;
+                       }
+                       if (byteIndex < numberOfBytes) {
+                               /*Then the next byte to the low bits. */
+                               unicode[unicodeIndex] |=
+                                   UDFCompressed[byteIndex++];
+                       }
+                       unicodeIndex++;
+               }
+               returnValue = unicodeIndex;
+       }
+       return(returnValue);
+}
+
+/***********************************************************************
+ * DESCRIPTION:
+ * Takes a string of unicode wide characters and returns an OSTA CS0
+ * compressed unicode string. The unicode MUST be in the byte order of
+ * the compiler in order to obtain correct results. Returns an error
+ * if the compression ID is invalid.
+ *
+ * NOTE: This routine assumes the implementation already knows, by
+ * the local environment, how many bits are appropriate and
+ * therefore does no checking to test if the input characters fit
+ * into that number of bits or not.
+ *
+ * RETURN VALUE
+ *
+ * The total number of bytes in the compressed OSTA CS0 string,
+ * including the compression ID.
+ * A -1 is returned if the compression ID is invalid.
+ */
+int
+udf_CompressUnicode(
+       int numberOfChars,      /* (Input) number of unicode characters. */
+       int compID,             /* (Input) compression ID to be used. */
+       unicode_t *unicode,     /* (Input) unicode characters to compress. */
+       byte *UDFCompressed)    /* (Output) compressed string, as bytes. */
+{
+       int byteIndex, unicodeIndex;
+
+       if (compID != 8 && compID != 16) {
+               byteIndex = -1; /* Unsupported compression ID ! */
+       } else {
+               /* Place compression code in first byte. */
+               UDFCompressed[0] = compID;
+
+               byteIndex = 1;
+               unicodeIndex = 0;
+               while (unicodeIndex < numberOfChars) {
+                       if (compID == 16) {
+                               /* First, place the high bits of the char
+                                * into the byte stream.
+                                */
+                               UDFCompressed[byteIndex++] =
+                                   (unicode[unicodeIndex] & 0xFF00) >> 8;
+                       }
+                       /*Then place the low bits into the stream. */
+                       UDFCompressed[byteIndex++] =
+                           unicode[unicodeIndex] & 0x00FF;
+                       unicodeIndex++;
+               }
+       }
+       return(byteIndex);
+}
+
+/*****************************************************************************/
+/*
+ * CRC 010041
+ */
+static unsigned short crc_table[256] = {
+       0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+       0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+       0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+       0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+       0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+       0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+       0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+       0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+       0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+       0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+       0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+       0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+       0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+       0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+       0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+       0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+       0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+       0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+       0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+       0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+       0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+       0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+       0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+       0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+       0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+       0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+       0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+       0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+       0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+       0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+       0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+       0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+
+unsigned short
+udf_cksum(unsigned char *s, int n)
+{
+       unsigned short crc=0;
+
+       while (n-- > 0)
+               crc = crc_table[(crc>>8 ^ *s++) & 0xff] ^ (crc<<8);
+       return crc;
+}
+
+/* UNICODE Checksum */
+unsigned short
+udf_unicode_cksum(unsigned short *s, int n)
+{
+       unsigned short crc=0;
+
+       while (n-- > 0) {
+               /* Take high order byte first--corresponds to a big endian
+                * byte stream.
+                */
+               crc = crc_table[(crc>>8 ^ (*s>>8)) & 0xff] ^ (crc<<8);
+               crc = crc_table[(crc>>8 ^ (*s++ & 0xff)) & 0xff] ^ (crc<<8);
+       }
+       return crc;
+}
+
+
+/*
+  * Calculates a 16-bit checksum of the Implementation Use
+  * Extended Attribute header or Application Use Extended Attribute
+  * header. The fields AttributeType through ImplementationIdentifier
+  * (or ApplicationIdentifier) inclusively represent the
+  * data covered by the checksum (48 bytes).
+  *
+  */
+uint16_t udf_ea_cksum(uint8_t *data) {
+        uint16_t checksum = 0;
+        int      count;
+
+        for (count = 0; count < 48; count++) {
+               checksum += *data++;
+        }
+
+        return checksum;
+}
+
+
+#ifdef MAIN
+unsigned char bytes[] = { 0x70, 0x6A, 0x77 };
+
+main(void)
+{
+       unsigned short x;
+       x = cksum(bytes, sizeof bytes);
+       printf("checksum: calculated=%4.4x, correct=%4.4x\en", x, 0x3299);
+       exit(0);
+}
+#endif
+
+/*****************************************************************************/
+/* #ifdef NEEDS_ISPRINT */
+/***********************************************************************
+ * OSTA UDF compliant file name translation routine for OS/2,
+ * Windows 95, Windows NT, Macintosh and UNIX.
+ * Copyright 1995 Micro Design International, Inc.
+ * Written by Jason M. Rinn.
+ * Micro Design International gives permission for the free use of the
+ * following source code.
+ */
+
+/***********************************************************************
+ * To use these routines with different operating systems.
+ *
+ * OS/2
+ * Define OS2
+ * Define MAXLEN = 254
+ *
+ * Windows 95
+ * Define WIN_95
+ * Define MAXLEN = 255
+ *
+ * Windows NT
+ * Define WIN_NT
+ * Define MAXLEN = 255
+ *
+ * Macintosh:
+ * Define MAC.
+ * Define MAXLEN = 31.
+ *
+ * UNIX
+ * Define UNIX.
+ * Define MAXLEN as specified by unix version.
+ */
+
+#define        ILLEGAL_CHAR_MARK       0x005F
+#define        CRC_MARK        0x0023
+#define        EXT_SIZE        5
+#define        PERIOD  0x002E
+#define        SPACE   0x0020
+
+/*** PROTOTYPES ***/
+int IsIllegal(unicode_t ch);
+
+/* Define a function or macro which determines if a Unicode character is
+ * printable under your implementation.
+ */
+
+
+/* #include <stdio.h> */
+static int UnicodeIsPrint(unicode_t ch) {
+       return (ch >=' ') && (ch != 127);
+}
+
+
+int UnicodeLength(unicode_t *string) {
+       int length;
+       length = 0;
+       while (*string++) length++;
+
+       return length;
+}
+
+
+#ifdef _KERNEL
+static int isprint(int c) {
+       return (c >= ' ') && (c != 127);
+}
+#endif
+
+
+/***********************************************************************
+ * Translates a long file name to one using a MAXLEN and an illegal
+ * char set in accord with the OSTA requirements. Assumes the name has
+ * already been translated to Unicode.
+ *
+ * RETURN VALUE
+ *
+ * Number of unicode characters in translated name.
+ */
+int UDFTransName(
+       unicode_t *newName,     /* (Output)Translated name. Must be of length
+                                * MAXLEN */
+       unicode_t *udfName,     /* (Input) Name from UDF volume.*/
+       int udfLen)             /* (Input) Length of UDF Name. */
+{
+       int Index, newIndex = 0, needsCRC = false;      /* index is shadowed */
+       int extIndex = 0, newExtIndex = 0, hasExt = false;
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+       int trailIndex = 0;
+#endif
+       unsigned short valueCRC;
+       unicode_t current;
+       const char hexChar[] = "0123456789ABCDEF";
+
+       for (Index = 0; Index < udfLen; Index++) {
+               current = udfName[Index];
+
+               if (IsIllegal(current) || !UnicodeIsPrint(current)) {
+                       needsCRC = true;
+                       /* Replace Illegal and non-displayable chars with
+                        * underscore.
+                        */
+                       current = ILLEGAL_CHAR_MARK;
+                       /* Skip any other illegal or non-displayable
+                        * characters.
+                        */
+                       while(Index+1 < udfLen && (IsIllegal(udfName[Index+1])
+                           || !UnicodeIsPrint(udfName[Index+1]))) {
+                               Index++;
+                       }
+               }
+
+               /* Record position of extension, if one is found. */
+               if (current == PERIOD && (udfLen - Index -1) <= EXT_SIZE) {
+                       if (udfLen == Index + 1) {
+                               /* A trailing period is NOT an extension. */
+                               hasExt = false;
+                       } else {
+                               hasExt = true;
+                               extIndex = Index;
+                               newExtIndex = newIndex;
+                       }
+               }
+
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+               /* Record position of last char which is NOT period or space. */
+               else if (current != PERIOD && current != SPACE) {
+                       trailIndex = newIndex;
+               }
+#endif
+
+               if (newIndex < MAXLEN) {
+                       newName[newIndex++] = current;
+               } else {
+                       needsCRC = true;
+               }
+       }
+
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+       /* For OS2, 95 & NT, truncate any trailing periods and\or spaces. */
+       if (trailIndex != newIndex - 1) {
+               newIndex = trailIndex + 1;
+               needsCRC = true;
+               hasExt = false; /* Trailing period does not make an
+                                * extension. */
+       }
+#endif
+
+       if (needsCRC) {
+               unicode_t ext[EXT_SIZE];
+               int localExtIndex = 0;
+               if (hasExt) {
+                       int maxFilenameLen;
+                       /* Translate extension, and store it in ext. */
+                       for(Index = 0; Index<EXT_SIZE &&
+                           extIndex + Index +1 < udfLen; Index++ ) {
+                               current = udfName[extIndex + Index + 1];
+                               if (IsIllegal(current) ||
+                                   !UnicodeIsPrint(current)) {
+                                       needsCRC = 1;
+                                       /* Replace Illegal and non-displayable
+                                        * chars with underscore.
+                                        */
+                                       current = ILLEGAL_CHAR_MARK;
+                                       /* Skip any other illegal or
+                                        * non-displayable characters.
+                                        */
+                                       while(Index + 1 < EXT_SIZE
+                                           && (IsIllegal(udfName[extIndex +
+                                           Index + 2]) ||
+                                           !isprint(udfName[extIndex +
+                                           Index + 2]))) {
+                                               Index++;
+                                       }
+                               }
+                               ext[localExtIndex++] = current;
+                       }
+
+                       /* Truncate filename to leave room for extension and
+                        * CRC.
+                        */
+                       maxFilenameLen = ((MAXLEN - 5) - localExtIndex - 1);
+                       if (newIndex > maxFilenameLen) {
+                               newIndex = maxFilenameLen;
+                       } else {
+                               newIndex = newExtIndex;
+                       }
+               } else if (newIndex > MAXLEN - 5) {
+                       /*If no extension, make sure to leave room for CRC. */
+                       newIndex = MAXLEN - 5;
+               }
+               newName[newIndex++] = CRC_MARK; /* Add mark for CRC. */
+
+               /*Calculate CRC from original filename from FileIdentifier. */
+               valueCRC = udf_unicode_cksum(udfName, udfLen);
+               /* Convert 16-bits of CRC to hex characters. */
+               newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12];
+               newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8];
+               newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4];
+               newName[newIndex++] = hexChar[(valueCRC & 0x000f)];
+
+               /* Place a translated extension at end, if found. */
+               if (hasExt) {
+                       newName[newIndex++] = PERIOD;
+                       for (Index = 0;Index < localExtIndex ;Index++ ) {
+                               newName[newIndex++] = ext[Index];
+                       }
+               }
+       }
+       return(newIndex);
+}
+
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+/***********************************************************************
+ * Decides if a Unicode character matches one of a list
+ * of ASCII characters.
+ * Used by OS2 version of IsIllegal for readability, since all of the
+ * illegal characters above 0x0020 are in the ASCII subset of Unicode.
+ * Works very similarly to the standard C function strchr().
+ *
+ * RETURN VALUE
+ *
+ * Non-zero if the Unicode character is in the given ASCII string.
+ */
+int UnicodeInString(
+       unsigned char *string,  /* (Input) String to search through. */
+       unicode_t ch)           /* (Input) Unicode char to search for. */
+{
+       int found = false;
+       while (*string != '\0' && found == false) {
+               /* These types should compare, since both are unsigned
+                * numbers. */
+               if (*string == ch) {
+                       found = true;
+               }
+               string++;
+       }
+       return(found);
+}
+#endif /* OS2 */
+
+/***********************************************************************
+ * Decides whether the given character is illegal for a given OS.
+ *
+ * RETURN VALUE
+ *
+ * Non-zero if char is illegal.
+ */
+int IsIllegal(unicode_t ch)
+{
+#ifdef MAC
+       /* Only illegal character on the MAC is the colon. */
+       if (ch == 0x003A) {
+               return(1);
+       } else {
+               return(0);
+       }
+
+#elif defined UNIX
+       /* Illegal UNIX characters are NULL and slash. */
+       if (ch == 0x0000 || ch == 0x002F) {
+               return(1);
+       } else {
+               return(0);
+       }
+
+#elif defined OS2 || defined WIN_95 || defined WIN_NT
+       /* Illegal char's for OS/2 according to WARP toolkit. */
+       if (ch < 0x0020 || UnicodeInString("\\/:*?\"<>|", ch)) {
+               return(1);
+       } else {
+               return(0);
+       }
+#endif
+}
+/* #endif*/    /* NEEDS_ISPRINT */
+
diff --git a/sys/fs/udf/udf_osta.h b/sys/fs/udf/udf_osta.h
new file mode 100644 (file)
index 0000000..4d166b7
--- /dev/null
@@ -0,0 +1,44 @@
+/* $NetBSD: udf_osta.h,v 1.4 2008/05/14 16:49:48 reinoud Exp $ */
+
+/*
+ * Prototypes for the OSTA functions
+ */
+
+
+#ifndef _FS_UDF_OSTA_H_
+#define _FS_UDF_OSTA_H_
+
+#include <sys/types.h>
+
+#ifndef _KERNEL
+#include <stdbool.h>
+#endif
+
+#ifndef UNIX
+#define        UNIX
+#endif
+
+#ifndef MAXLEN
+#define        MAXLEN  255
+#endif
+
+
+/***********************************************************************
+ * The following two typedef's are to remove compiler dependancies.
+ * byte needs to be unsigned 8-bit, and unicode_t needs to be
+ * unsigned 16-bit.
+ */
+typedef uint16_t unicode_t;
+typedef uint8_t    byte;
+
+
+int udf_UncompressUnicode(int, byte *, unicode_t *);
+int udf_CompressUnicode(int, int, unicode_t *, byte *);
+unsigned short udf_cksum(unsigned char *, int);
+unsigned short udf_unicode_cksum(unsigned short *, int);
+uint16_t udf_ea_cksum(uint8_t *data);
+int UDFTransName(unicode_t *, unicode_t *, int);
+int UnicodeLength(unicode_t *string);
+
+
+#endif /* _FS_UDF_OSTA_H_ */
diff --git a/sys/fs/udf/udf_readwrite.c b/sys/fs/udf/udf_readwrite.c
new file mode 100644 (file)
index 0000000..57c4ee2
--- /dev/null
@@ -0,0 +1,723 @@
+/* $NetBSD: udf_readwrite.c,v 1.11 2011/06/12 03:35:55 rmind Exp $ */
+
+/*
+ * Copyright (c) 2007, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__KERNEL_RCSID(0, "$NetBSD: udf_readwrite.c,v 1.11 2011/06/12 03:35:55 rmind Exp $");
+#endif /* not lint */
+
+
+#if defined(_KERNEL_OPT)
+#include "opt_compat_netbsd.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#include "udf.h"
+#include "udf_subr.h"
+#include "udf_bswap.h"
+
+
+#define VTOI(vnode) ((struct udf_node *) vnode->v_data)
+
+/* --------------------------------------------------------------------- */
+
+void
+udf_fixup_fid_block(uint8_t *blob, int lb_size,
+       int rfix_pos, int max_rfix_pos, uint32_t lb_num)
+{
+       struct fileid_desc *fid;
+       uint8_t *fid_pos;
+       int fid_len, found;
+
+       /* needs to be word aligned */
+       KASSERT(rfix_pos % 4 == 0);
+
+       /* first resync with the FID stream !!! */
+       found = 0;
+       while (rfix_pos + sizeof(struct desc_tag) <= max_rfix_pos) {
+               fid_pos = blob + rfix_pos;
+               fid = (struct fileid_desc *) fid_pos;
+               if (udf_rw16(fid->tag.id) == TAGID_FID) {
+                       if (udf_check_tag((union dscrptr *) fid) == 0)
+                               found = 1;
+               }
+               if (found)
+                       break;
+               /* try next location; can only be 4 bytes aligned */
+               rfix_pos += 4;
+       }
+
+       /* walk over the fids */
+       fid_pos = blob + rfix_pos;
+       while (rfix_pos + sizeof(struct desc_tag) <= max_rfix_pos) {
+               fid = (struct fileid_desc *) fid_pos;
+               if (udf_rw16(fid->tag.id) != TAGID_FID) {
+                       /* end of FID stream; end of directory or currupted */
+                       break;
+               }
+
+               /* update sector number and recalculate checkum */
+               fid->tag.tag_loc = udf_rw32(lb_num);
+               udf_validate_tag_sum((union dscrptr *) fid);
+
+               /* if the FID crosses the memory, we're done! */
+               if (rfix_pos + UDF_FID_SIZE >= max_rfix_pos)
+                       break;
+
+               fid_len = udf_fidsize(fid);
+               fid_pos  += fid_len;
+               rfix_pos += fid_len;
+       }
+}
+
+
+void
+udf_fixup_internal_extattr(uint8_t *blob, uint32_t lb_num)
+{
+       struct desc_tag        *tag;
+       struct file_entry      *fe;
+       struct extfile_entry   *efe;
+       struct extattrhdr_desc *eahdr;
+       int l_ea;
+
+       /* get information from fe/efe */
+       tag = (struct desc_tag *) blob;
+       switch (udf_rw16(tag->id)) {
+       case TAGID_FENTRY :
+               fe = (struct file_entry *) blob;
+               l_ea  = udf_rw32(fe->l_ea);
+               eahdr = (struct extattrhdr_desc *) fe->data;
+               break;
+       case TAGID_EXTFENTRY :
+               efe = (struct extfile_entry *) blob;
+               l_ea  = udf_rw32(efe->l_ea);
+               eahdr = (struct extattrhdr_desc *) efe->data;
+               break;
+       case TAGID_INDIRECTENTRY :
+       case TAGID_ALLOCEXTENT :
+       case TAGID_EXTATTR_HDR :
+               return;
+       default:
+               panic("%s: passed bad tag\n", __func__);
+       }
+
+       /* something recorded here? (why am i called?) */
+       if (l_ea == 0)
+               return;
+
+#if 0
+       /* check extended attribute tag */
+       /* TODO XXX what to do when we encounter an error here? */
+       error = udf_check_tag(eahdr);
+       if (error)
+               return; /* for now */
+       if (udf_rw16(eahdr->tag.id) != TAGID_EXTATTR_HDR)
+               return; /* for now */
+       error = udf_check_tag_payload(eahdr, sizeof(struct extattrhdr_desc));
+       if (error)
+               return; /* for now */
+#endif
+
+       DPRINTF(EXTATTR, ("node fixup: found %d bytes of extended attributes\n",
+               l_ea));
+
+       /* fixup eahdr tag */
+       eahdr->tag.tag_loc = udf_rw32(lb_num);
+       udf_validate_tag_and_crc_sums((union dscrptr *) eahdr);
+}
+
+
+void
+udf_fixup_node_internals(struct udf_mount *ump, uint8_t *blob, int udf_c_type)
+{
+       struct desc_tag *tag, *sbm_tag;
+       struct file_entry *fe;
+       struct extfile_entry *efe;
+       struct alloc_ext_entry *ext;
+       uint32_t lb_size, lb_num;
+       uint32_t intern_pos, max_intern_pos;
+       int icbflags, addr_type, file_type, intern, has_fids, has_sbm, l_ea;
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       /* if its not a node we're done */
+       if (udf_c_type != UDF_C_NODE)
+               return;
+
+       /* NOTE this could also be done in write_internal */
+       /* start of a descriptor */
+       l_ea      = 0;
+       has_fids  = 0;
+       has_sbm   = 0;
+       intern    = 0;
+       file_type = 0;
+       max_intern_pos = intern_pos = lb_num = 0;       /* shut up gcc! */
+
+       tag = (struct desc_tag *) blob;
+       switch (udf_rw16(tag->id)) {
+       case TAGID_FENTRY :
+               fe = (struct file_entry *) tag;
+               l_ea = udf_rw32(fe->l_ea);
+               icbflags  = udf_rw16(fe->icbtag.flags);
+               addr_type = (icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK);
+               file_type = fe->icbtag.file_type;
+               intern = (addr_type == UDF_ICB_INTERN_ALLOC);
+               intern_pos  = UDF_FENTRY_SIZE + l_ea;
+               max_intern_pos = intern_pos + udf_rw64(fe->inf_len);
+               lb_num = udf_rw32(fe->tag.tag_loc);
+               break;
+       case TAGID_EXTFENTRY :
+               efe = (struct extfile_entry *) tag;
+               l_ea = udf_rw32(efe->l_ea);
+               icbflags  = udf_rw16(efe->icbtag.flags);
+               addr_type = (icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK);
+               file_type = efe->icbtag.file_type;
+               intern = (addr_type == UDF_ICB_INTERN_ALLOC);
+               intern_pos  = UDF_EXTFENTRY_SIZE + l_ea;
+               max_intern_pos = intern_pos + udf_rw64(efe->inf_len);
+               lb_num = udf_rw32(efe->tag.tag_loc);
+               break;
+       case TAGID_INDIRECTENTRY :
+       case TAGID_EXTATTR_HDR :
+               break;
+       case TAGID_ALLOCEXTENT :
+               /* force crclen to 8 for UDF version < 2.01 */
+               ext = (struct alloc_ext_entry *) tag;
+               if (udf_rw16(ump->logvol_info->min_udf_readver) <= 0x200)
+                       ext->tag.desc_crc_len = udf_rw16(8);
+               break;
+       default:
+               panic("%s: passed bad tag\n", __func__);
+               break;
+       }
+
+       /* determine what to fix if its internally recorded */
+       if (intern) {
+               has_fids = (file_type == UDF_ICB_FILETYPE_DIRECTORY) ||
+                          (file_type == UDF_ICB_FILETYPE_STREAMDIR);
+               has_sbm  = (file_type == UDF_ICB_FILETYPE_META_BITMAP);
+       }
+
+       /* fixup internal extended attributes if present */
+       if (l_ea)
+               udf_fixup_internal_extattr(blob, lb_num);
+
+       /* fixup fids lb numbers */
+       if (has_fids)
+               udf_fixup_fid_block(blob, lb_size, intern_pos,
+                       max_intern_pos, lb_num);
+
+       /* fixup space bitmap descriptor */
+       if (has_sbm) {
+               sbm_tag = (struct desc_tag *) (blob + intern_pos);
+               sbm_tag->tag_loc = tag->tag_loc;
+               udf_validate_tag_and_crc_sums((uint8_t *) sbm_tag);
+       }
+
+       udf_validate_tag_and_crc_sums(blob);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Set of generic descriptor readers and writers and their helper functions.
+ * Descriptors inside `logical space' i.e. inside logically mapped partitions
+ * can never be longer than one logical sector.
+ *
+ * NOTE that these functions *can* be used by the sheduler backends to read
+ * node descriptors too.
+ *
+ * For reading, the size of allocated piece is returned in multiple of sector
+ * size due to udf_calc_udf_malloc_size().
+ */
+
+
+/* SYNC reading of n blocks from specified sector */
+int
+udf_read_phys_sectors(struct udf_mount *ump, int what, void *blob,
+       uint32_t start, uint32_t sectors)
+{
+       struct buf *buf, *nestbuf;
+       uint32_t buf_offset;
+       off_t lblkno, rblkno;
+       int sector_size = ump->discinfo.sector_size;
+       int blks = sector_size / DEV_BSIZE;
+       int piece;
+       int error;
+
+       DPRINTF(READ, ("udf_intbreadn() : sectors = %d, sector_size = %d\n",
+               sectors, sector_size));
+       buf = getiobuf(ump->devvp, true);
+       buf->b_flags    = B_READ;
+       buf->b_cflags   = BC_BUSY;      /* needed? */
+       buf->b_iodone   = NULL;
+       buf->b_data     = blob;
+       buf->b_bcount   = sectors * sector_size;
+       buf->b_resid    = buf->b_bcount;
+       buf->b_bufsize  = buf->b_bcount;
+       buf->b_private  = NULL; /* not needed yet */
+       BIO_SETPRIO(buf, BPRIO_DEFAULT);
+       buf->b_lblkno   = buf->b_blkno = buf->b_rawblkno = start * blks;
+       buf->b_proc     = NULL;
+
+       error = 0;
+       buf_offset = 0;
+       rblkno = start;
+       lblkno = 0;
+       while ((sectors > 0) && (error == 0)) {
+               piece = MIN(MAXPHYS/sector_size, sectors);
+               DPRINTF(READ, ("read in %d + %d\n", (uint32_t) rblkno, piece));
+
+               nestbuf = getiobuf(NULL, true);
+               nestiobuf_setup(buf, nestbuf, buf_offset, piece * sector_size);
+               /* nestbuf is B_ASYNC */
+
+               /* identify this nestbuf */
+               nestbuf->b_lblkno   = lblkno;
+
+               /* CD shedules on raw blkno */
+               nestbuf->b_blkno      = rblkno * blks;
+               nestbuf->b_proc       = NULL;
+               nestbuf->b_rawblkno   = rblkno * blks;
+               nestbuf->b_udf_c_type = what;
+
+               udf_discstrat_queuebuf(ump, nestbuf);
+
+               lblkno     += piece;
+               rblkno     += piece;
+               buf_offset += piece * sector_size;
+               sectors    -= piece;
+       }
+       error = biowait(buf);
+       putiobuf(buf);
+
+       return error;
+}
+
+
+/* synchronous generic descriptor read */
+int
+udf_read_phys_dscr(struct udf_mount *ump, uint32_t sector,
+                   struct malloc_type *mtype, union dscrptr **dstp)
+{
+       union dscrptr *dst, *new_dst;
+       uint8_t *pos;
+       int sectors, dscrlen;
+       int i, error, sector_size;
+
+       sector_size = ump->discinfo.sector_size;
+
+       *dstp = dst = NULL;
+       dscrlen = sector_size;
+
+       /* read initial piece */
+       dst = malloc(sector_size, mtype, M_WAITOK);
+       error = udf_read_phys_sectors(ump, UDF_C_DSCR, dst, sector, 1);
+       DPRINTFIF(DESCRIPTOR, error, ("read error (%d)\n", error));
+
+       if (!error) {
+               /* check if its a valid tag */
+               error = udf_check_tag(dst);
+               if (error) {
+                       /* check if its an empty block */
+                       pos = (uint8_t *) dst;
+                       for (i = 0; i < sector_size; i++, pos++) {
+                               if (*pos) break;
+                       }
+                       if (i == sector_size) {
+                               /* return no error but with no dscrptr */
+                               /* dispose first block */
+                               free(dst, mtype);
+                               return 0;
+                       }
+               }
+               /* calculate descriptor size */
+               dscrlen = udf_tagsize(dst, sector_size);
+       }
+       DPRINTFIF(DESCRIPTOR, error, ("bad tag checksum\n"));
+
+       if (!error && (dscrlen > sector_size)) {
+               DPRINTF(DESCRIPTOR, ("multi block descriptor read\n"));
+               /*
+                * Read the rest of descriptor. Since it is only used at mount
+                * time its overdone to define and use a specific udf_intbreadn
+                * for this alone.
+                */
+
+               new_dst = realloc(dst, dscrlen, mtype, M_WAITOK);
+               if (new_dst == NULL) {
+                       free(dst, mtype);
+                       return ENOMEM;
+               }
+               dst = new_dst;
+
+               sectors = (dscrlen + sector_size -1) / sector_size;
+               DPRINTF(DESCRIPTOR, ("dscrlen = %d (%d blk)\n", dscrlen, sectors));
+       
+               pos = (uint8_t *) dst + sector_size;
+               error = udf_read_phys_sectors(ump, UDF_C_DSCR, pos,
+                               sector + 1, sectors-1);
+
+               DPRINTFIF(DESCRIPTOR, error, ("read error on multi (%d)\n",
+                   error));
+       }
+       if (!error) {
+               error = udf_check_tag_payload(dst, dscrlen);
+               DPRINTFIF(DESCRIPTOR, error, ("bad payload check sum\n"));
+       }
+       if (error && dst) {
+               free(dst, mtype);
+               dst = NULL;
+       }
+       *dstp = dst;
+
+       return error;
+}
+
+
+static void
+udf_write_phys_buf(struct udf_mount *ump, int what, struct buf *buf)
+{
+       struct buf *nestbuf;
+       uint32_t buf_offset;
+       off_t lblkno, rblkno;
+       int sector_size = ump->discinfo.sector_size;
+       int blks = sector_size / DEV_BSIZE;
+       uint32_t sectors;
+       int piece;
+       int error;
+
+       sectors = buf->b_bcount / sector_size;
+       DPRINTF(WRITE, ("udf_intbwriten() : sectors = %d, sector_size = %d\n",
+               sectors, sector_size));
+
+       /* don't forget to increase pending count for the bwrite itself */
+/* panic("NO WRITING\n"); */
+       if (buf->b_vp) {
+               mutex_enter(buf->b_vp->v_interlock);
+               buf->b_vp->v_numoutput++;
+               mutex_exit(buf->b_vp->v_interlock);
+       }
+
+       error = 0;
+       buf_offset = 0;
+       rblkno = buf->b_blkno / blks;
+       lblkno = 0;
+       while ((sectors > 0) && (error == 0)) {
+               piece = MIN(MAXPHYS/sector_size, sectors);
+               DPRINTF(WRITE, ("write out %d + %d\n",
+                   (uint32_t) rblkno, piece));
+
+               nestbuf = getiobuf(NULL, true);
+               nestiobuf_setup(buf, nestbuf, buf_offset, piece * sector_size);
+               /* nestbuf is B_ASYNC */
+
+               /* identify this nestbuf */
+               nestbuf->b_lblkno   = lblkno;
+
+               /* CD shedules on raw blkno */
+               nestbuf->b_blkno      = rblkno * blks;
+               nestbuf->b_proc       = NULL;
+               nestbuf->b_rawblkno   = rblkno * blks;
+               nestbuf->b_udf_c_type = what;
+
+               udf_discstrat_queuebuf(ump, nestbuf);
+
+               lblkno     += piece;
+               rblkno     += piece;
+               buf_offset += piece * sector_size;
+               sectors    -= piece;
+       }
+}
+
+
+/* SYNC writing of n blocks from specified sector */
+int
+udf_write_phys_sectors(struct udf_mount *ump, int what, void *blob,
+       uint32_t start, uint32_t sectors)
+{
+       struct vnode *vp;
+       struct buf *buf;
+       int sector_size = ump->discinfo.sector_size;
+       int blks = sector_size / DEV_BSIZE;
+       int error;
+
+       /* get transfer buffer */
+       vp = ump->devvp;
+       buf = getiobuf(vp, true);
+       buf->b_flags    = B_WRITE;
+       buf->b_cflags   = BC_BUSY;      /* needed? */
+       buf->b_iodone   = NULL;
+       buf->b_data     = blob;
+       buf->b_bcount   = sectors * sector_size;
+       buf->b_resid    = buf->b_bcount;
+       buf->b_bufsize  = buf->b_bcount;
+       buf->b_private  = NULL; /* not needed yet */
+       BIO_SETPRIO(buf, BPRIO_DEFAULT);
+       buf->b_lblkno   = buf->b_blkno = buf->b_rawblkno = start * blks;
+       buf->b_proc     = NULL;
+
+       /* do the write, wait and return error */
+       udf_write_phys_buf(ump, what, buf);
+       error = biowait(buf);
+       putiobuf(buf);
+
+       return error;
+}
+
+
+/* synchronous generic descriptor write */
+int
+udf_write_phys_dscr_sync(struct udf_mount *ump, struct udf_node *udf_node, int what,
+                    union dscrptr *dscr, uint32_t sector, uint32_t logsector)
+{
+       struct vnode *vp;
+       struct buf *buf;
+       int sector_size = ump->discinfo.sector_size;
+       int blks = sector_size / DEV_BSIZE;
+       int dscrlen;
+       int error;
+
+       /* set sector number in the descriptor and validate */
+       dscr->tag.tag_loc = udf_rw32(logsector);
+       udf_validate_tag_and_crc_sums(dscr);
+
+       /* calculate descriptor size */
+       dscrlen = udf_tagsize(dscr, sector_size);
+
+       /* get transfer buffer */
+       vp = udf_node ? udf_node->vnode : ump->devvp;
+       buf = getiobuf(vp, true);
+       buf->b_flags    = B_WRITE;
+       buf->b_cflags   = BC_BUSY;      /* needed? */
+       buf->b_iodone   = NULL;
+       buf->b_data     = (void *) dscr;
+       buf->b_bcount   = dscrlen;
+       buf->b_resid    = buf->b_bcount;
+       buf->b_bufsize  = buf->b_bcount;
+       buf->b_private  = NULL; /* not needed yet */
+       BIO_SETPRIO(buf, BPRIO_DEFAULT);
+       buf->b_lblkno   = buf->b_blkno = buf->b_rawblkno = sector * blks;
+       buf->b_proc     = NULL;
+
+       /* do the write, wait and return error */
+       udf_write_phys_buf(ump, what, buf);
+       error = biowait(buf);
+       putiobuf(buf);
+
+       return error;
+}
+
+
+/* asynchronous generic descriptor write */
+int
+udf_write_phys_dscr_async(struct udf_mount *ump, struct udf_node *udf_node,
+                     int what, union dscrptr *dscr,
+                     uint32_t sector, uint32_t logsector,
+                     void (*dscrwr_callback)(struct buf *))
+{
+       struct vnode *vp;
+       struct buf *buf;
+       int dscrlen;
+       int sector_size = ump->discinfo.sector_size;
+       int blks = sector_size / DEV_BSIZE;
+
+       KASSERT(dscrwr_callback);
+       DPRINTF(NODE, ("udf_write_phys_dscr_async() called\n"));
+
+       /* set sector number in the descriptor and validate */
+       dscr->tag.tag_loc = udf_rw32(logsector);
+       udf_validate_tag_and_crc_sums(dscr);
+
+       /* calculate descriptor size */
+       dscrlen = udf_tagsize(dscr, sector_size);
+
+       /* get transfer buffer */
+       vp = udf_node ? udf_node->vnode : ump->devvp;
+       buf = getiobuf(vp, true);
+       buf->b_flags    = B_WRITE; // | B_ASYNC;
+       buf->b_cflags   = BC_BUSY;
+       buf->b_iodone   = dscrwr_callback;
+       buf->b_data     = dscr;
+       buf->b_bcount   = dscrlen;
+       buf->b_resid    = buf->b_bcount;
+       buf->b_bufsize  = buf->b_bcount;
+       buf->b_private  = NULL; /* not needed yet */
+       BIO_SETPRIO(buf, BPRIO_DEFAULT);
+       buf->b_lblkno   = buf->b_blkno = buf->b_rawblkno = sector * blks;
+       buf->b_proc     = NULL;
+
+       /* do the write and return no error */
+       udf_write_phys_buf(ump, what, buf);
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* disc strategy dispatchers */
+
+int
+udf_create_logvol_dscr(struct udf_mount *ump, struct udf_node *udf_node, struct long_ad *icb,
+       union dscrptr **dscrptr)
+{
+       struct udf_strategy *strategy = ump->strategy;
+       struct udf_strat_args args;
+       int error;
+
+       KASSERT(strategy);
+       args.ump  = ump;
+       args.udf_node = udf_node;
+       args.icb  = icb;
+       args.dscr = NULL;
+
+       error = (strategy->create_logvol_dscr)(&args);
+       *dscrptr = args.dscr;
+
+       return error;
+}
+
+
+void
+udf_free_logvol_dscr(struct udf_mount *ump, struct long_ad *icb,
+       void *dscr)
+{
+       struct udf_strategy *strategy = ump->strategy;
+       struct udf_strat_args args;
+
+       KASSERT(strategy);
+       args.ump  = ump;
+       args.icb  = icb;
+       args.dscr = dscr;
+
+       (strategy->free_logvol_dscr)(&args);
+}
+
+
+int
+udf_read_logvol_dscr(struct udf_mount *ump, struct long_ad *icb,
+       union dscrptr **dscrptr)
+{
+       struct udf_strategy *strategy = ump->strategy;
+       struct udf_strat_args args;
+       int error;
+
+       KASSERT(strategy);
+       args.ump  = ump;
+       args.icb  = icb;
+       args.dscr = NULL;
+
+       error = (strategy->read_logvol_dscr)(&args);
+       *dscrptr = args.dscr;
+
+       return error;
+}
+
+
+int
+udf_write_logvol_dscr(struct udf_node *udf_node, union dscrptr *dscr,
+       struct long_ad *icb, int waitfor)
+{
+       struct udf_strategy *strategy = udf_node->ump->strategy;
+       struct udf_strat_args args;
+       int error;
+
+       KASSERT(strategy);
+       args.ump      = udf_node->ump;
+       args.udf_node = udf_node;
+       args.icb      = icb;
+       args.dscr     = dscr;
+       args.waitfor  = waitfor;
+
+       error = (strategy->write_logvol_dscr)(&args);
+       return error;
+}
+
+
+void
+udf_discstrat_queuebuf(struct udf_mount *ump, struct buf *nestbuf)
+{
+       struct udf_strategy *strategy = ump->strategy;
+       struct udf_strat_args args;
+
+       KASSERT(strategy);
+       args.ump = ump;
+       args.nestbuf = nestbuf;
+
+       (strategy->queuebuf)(&args);
+}
+
+
+void
+udf_discstrat_init(struct udf_mount *ump)
+{
+       struct udf_strategy *strategy = ump->strategy;
+       struct udf_strat_args args;
+
+       KASSERT(strategy);
+       args.ump = ump;
+       (strategy->discstrat_init)(&args);
+}
+
+
+void udf_discstrat_finish(struct udf_mount *ump)
+{
+       struct udf_strategy *strategy = ump->strategy;
+       struct udf_strat_args args;
+
+       /* strategy might not have been set, so ignore if not set */
+       if (strategy) {
+               args.ump = ump;
+               (strategy->discstrat_finish)(&args);
+       }
+}
+
+/* --------------------------------------------------------------------- */
+
diff --git a/sys/fs/udf/udf_rename.c b/sys/fs/udf/udf_rename.c
new file mode 100644 (file)
index 0000000..1aa6bb2
--- /dev/null
@@ -0,0 +1,669 @@
+/* $NetBSD: udf_rename.c,v 1.10 2013/07/16 10:49:36 reinoud Exp $ */
+
+/*
+ * Copyright (c) 2013 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ * Comments and trivial code from the reference implementation in tmpfs.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: udf_rename.c,v 1.10 2013/07/16 10:49:36 reinoud Exp $");
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/kauth.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/stat.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/vnode.h>
+#include <sys/vnode_if.h>
+
+#include <miscfs/genfs/genfs.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+#include <sys/dirhash.h>
+
+#include "udf.h"
+#include "udf_subr.h"
+#include "udf_bswap.h"
+
+
+/* forwards */
+static int udf_sane_rename( struct vnode *, struct componentname *,
+    struct vnode *, struct componentname *,
+    kauth_cred_t, bool);
+static bool udf_rmdired_p(struct vnode *);
+static int udf_gro_lock_directory(struct mount *, struct vnode *);
+
+static const struct genfs_rename_ops udf_genfs_rename_ops;
+
+
+#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data)
+
+
+/*
+ * udf_sane_rename: The hairiest vop, with the saner API.
+ *
+ * Arguments:
+ *
+ * . fdvp (from directory vnode),
+ * . fcnp (from component name),
+ * . tdvp (to directory vnode),
+ * . tcnp (to component name),
+ * . cred (credentials structure), and
+ * . posixly_correct (flag for behaviour if target & source link same file).
+ *
+ * fdvp and tdvp may be the same, and must be referenced and unlocked.
+ */
+static int
+udf_sane_rename( struct vnode *fdvp, struct componentname *fcnp,
+    struct vnode *tdvp, struct componentname *tcnp,
+    kauth_cred_t cred, bool posixly_correct)
+{
+       DPRINTF(CALL, ("udf_sane_rename '%s' -> '%s'\n",
+               fcnp->cn_nameptr, tcnp->cn_nameptr));
+       return genfs_sane_rename(&udf_genfs_rename_ops,
+           fdvp, fcnp, NULL, tdvp, tcnp, NULL,
+           cred, posixly_correct);
+}
+
+
+/*
+ * udf_rename: the hairiest vop, with the insanest API. Pass to
+ * genfs_insane_rename immediately.
+ */
+int
+udf_rename(void *v)
+{
+       struct vop_rename_args /* {
+               struct vnode *a_fdvp;
+               struct vnode *a_fvp;
+               struct componentname *a_fcnp;
+               struct vnode *a_tdvp;
+               struct vnode *a_tvp;
+               struct componentname *a_tcnp;
+       } */ *ap = v;
+       DPRINTF(CALL, ("udf_rename called\n"));
+       return genfs_insane_rename(ap, &udf_sane_rename);
+}
+
+
+/*
+ * udf_gro_directory_empty_p: return true if the directory vp is empty. dvp is
+ * its parent.
+ *
+ * vp and dvp must be locked and referenced.
+ */
+static bool
+udf_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred,
+    struct vnode *vp, struct vnode *dvp)
+{
+       struct udf_node *udf_node = VTOI(vp);
+       int error, isempty;
+
+       KASSERT(mp != NULL);
+       KASSERT(vp != NULL);
+       KASSERT(dvp != NULL);
+       KASSERT(vp != dvp);
+       KASSERT(vp->v_mount == mp);
+       KASSERT(dvp->v_mount == mp);
+       KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+       KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+
+       DPRINTF(CALL, ("udf_gro_directory_empty_p called\n"));
+       /* make sure our `leaf' node's hash is populated */
+       dirhash_get(&udf_node->dir_hash);
+       error = udf_dirhash_fill(udf_node);
+       if (error) {
+               dirhash_put(udf_node->dir_hash);
+               /* VERY unlikely, answer its not empty */
+               return 0;
+       }
+
+       /* check to see if the directory is empty */
+       isempty = dirhash_dir_isempty(udf_node->dir_hash);
+       dirhash_put(udf_node->dir_hash);
+
+       return isempty;
+}
+
+
+/*
+ * udf_gro_rename_check_possible: check whether a rename is possible
+ * independent of credentials.
+ */
+static int
+udf_gro_rename_check_possible(struct mount *mp,
+    struct vnode *fdvp, struct vnode *fvp,
+    struct vnode *tdvp, struct vnode *tvp)
+{
+       (void)mp;
+       KASSERT(mp != NULL);
+       KASSERT(fdvp != NULL);
+       KASSERT(fvp != NULL);
+       KASSERT(tdvp != NULL);
+       KASSERT(fdvp != fvp);
+       KASSERT(fdvp != tvp);
+       KASSERT(tdvp != fvp);
+       KASSERT(tdvp != tvp);
+       KASSERT(fvp != tvp);
+       KASSERT(fdvp->v_type == VDIR);
+       KASSERT(tdvp->v_type == VDIR);
+       KASSERT(fdvp->v_mount == mp);
+       KASSERT(fvp->v_mount == mp);
+       KASSERT(tdvp->v_mount == mp);
+       KASSERT((tvp == NULL) || (tvp->v_mount == mp));
+       KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
+       KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
+       KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
+       KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
+
+       DPRINTF(CALL, ("udf_gro_rename_check_possible called\n"));
+
+       /* flags not implemented since they are not defined (yet) in UDF */
+       return 0;
+}
+
+
+/*
+ * udf_gro_rename_check_permitted: check whether a rename is permitted given
+ * our credentials.
+ */
+static int
+udf_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred,
+    struct vnode *fdvp, struct vnode *fvp,
+    struct vnode *tdvp, struct vnode *tvp)
+{
+       struct udf_node *fdir_node = VTOI(fdvp);
+       struct udf_node *tdir_node = VTOI(tdvp);
+       struct udf_node *f_node = VTOI(fvp);
+       struct udf_node *t_node = (tvp? VTOI(tvp): NULL);
+       mode_t fdmode, tdmode;
+       uid_t fduid, tduid, fuid, tuid;
+       gid_t gdummy;
+
+       (void)mp;
+       KASSERT(mp != NULL);
+       KASSERT(fdvp != NULL);
+       KASSERT(fvp != NULL);
+       KASSERT(tdvp != NULL);
+       KASSERT(fdvp != fvp);
+       KASSERT(fdvp != tvp);
+       KASSERT(tdvp != fvp);
+       KASSERT(tdvp != tvp);
+       KASSERT(fvp != tvp);
+       KASSERT(fdvp->v_type == VDIR);
+       KASSERT(tdvp->v_type == VDIR);
+       KASSERT(fdvp->v_mount == mp);
+       KASSERT(fvp->v_mount == mp);
+       KASSERT(tdvp->v_mount == mp);
+       KASSERT((tvp == NULL) || (tvp->v_mount == mp));
+       KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
+       KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
+       KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
+       KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
+
+       DPRINTF(CALL, ("udf_gro_rename_check_permitted called\n"));
+       fdmode = udf_getaccessmode(fdir_node);
+       tdmode = udf_getaccessmode(tdir_node);
+
+       udf_getownership(fdir_node, &fduid, &gdummy);
+       udf_getownership(tdir_node, &tduid, &gdummy);
+       udf_getownership(f_node,    &fuid, &gdummy);
+
+       tuid = 0;
+       if (t_node)
+               udf_getownership(t_node, &tuid, &gdummy);
+
+       return genfs_ufslike_rename_check_permitted(cred,
+           fdvp, fdmode, fduid,
+           fvp,  fuid,
+           tdvp, tdmode, tduid,
+           tvp,  tuid);
+}
+
+
+/*
+ * udf_gro_remove_check_possible: check whether a remove is possible
+ * independent of credentials.
+ *
+ * XXX could check for special attributes?
+ */
+static int
+udf_gro_remove_check_possible(struct mount *mp,
+    struct vnode *dvp, struct vnode *vp)
+{
+       (void)mp;
+       KASSERT(mp != NULL);
+       KASSERT(dvp != NULL);
+       KASSERT(vp != NULL);
+       KASSERT(dvp != vp);
+       KASSERT(dvp->v_type == VDIR);
+       KASSERT(vp->v_type != VDIR);
+       KASSERT(dvp->v_mount == mp);
+       KASSERT(vp->v_mount == mp);
+       KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+       KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+
+       DPRINTF(CALL, ("udf_gro_remove_check_possible called\n"));
+
+       /* flags not implemented since they are not defined (yet) in UDF */
+       return 0;
+}
+
+
+/*
+ * udf_gro_remove_check_permitted: check whether a remove is permitted given
+ * our credentials.
+ */
+static int
+udf_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred,
+    struct vnode *dvp, struct vnode *vp)
+{
+       struct udf_node *dir_node = VTOI(dvp);
+       struct udf_node *udf_node = VTOI(vp);
+       mode_t dmode;
+       uid_t duid, uid;
+       gid_t gdummy;
+
+       (void)mp;
+       KASSERT(mp != NULL);
+       KASSERT(dvp != NULL);
+       KASSERT(vp != NULL);
+       KASSERT(dvp != vp);
+       KASSERT(dvp->v_type == VDIR);
+       KASSERT(vp->v_type != VDIR);
+       KASSERT(dvp->v_mount == mp);
+       KASSERT(vp->v_mount == mp);
+       KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+       KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+
+       DPRINTF(CALL, ("udf_gro_remove_check_permitted called\n"));
+       dmode = udf_getaccessmode(dir_node);
+
+       udf_getownership(dir_node, &duid, &gdummy);
+       udf_getownership(udf_node, &uid,  &gdummy);
+
+       return genfs_ufslike_remove_check_permitted(cred,
+           dvp, dmode, duid,
+           vp, uid);
+}
+
+
+/*
+ * udf_gro_rename: actually perform the rename operation.
+ */
+static int
+udf_gro_rename(struct mount *mp, kauth_cred_t cred,
+    struct vnode *fdvp, struct componentname *fcnp,
+    void *fde, struct vnode *fvp,
+    struct vnode *tdvp, struct componentname *tcnp,
+    void *tde, struct vnode *tvp)
+{
+       struct udf_node *fnode, *fdnode, *tnode, *tdnode;
+       struct vattr fvap;
+       int error;
+
+       (void)cred;
+       KASSERT(mp != NULL);
+       KASSERT(fdvp != NULL);
+       KASSERT(fcnp != NULL);
+       KASSERT(fvp != NULL);
+       KASSERT(tdvp != NULL);
+       KASSERT(tcnp != NULL);
+       KASSERT(fdvp != fvp);
+       KASSERT(fdvp != tvp);
+       KASSERT(tdvp != fvp);
+       KASSERT(tdvp != tvp);
+       KASSERT(fvp != tvp);
+       KASSERT(fdvp->v_mount == mp);
+       KASSERT(fvp->v_mount == mp);
+       KASSERT(tdvp->v_mount == mp);
+       KASSERT((tvp == NULL) || (tvp->v_mount == mp));
+       KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
+       KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
+       KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
+       KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
+
+       DPRINTF(CALL, ("udf_gro_rename called\n"));
+       DPRINTF(NODE, ("udf_gro_rename called : %s -> %s\n",
+               fcnp->cn_nameptr, tcnp->cn_nameptr));
+
+       fnode  = VTOI(fvp);
+       fdnode = VTOI(fdvp);
+       tnode  = (tvp == NULL) ? NULL : VTOI(tvp);
+       tdnode = VTOI(tdvp);
+
+       /* get attribute information */
+       error = VOP_GETATTR(fvp, &fvap, NULL);
+       if (error)
+               return error;
+
+       /* remove existing entry if present */
+       if (tvp) 
+               udf_dir_detach(tdnode->ump, tdnode, tnode, tcnp);
+
+       /* create new directory entry for the node */
+       error = udf_dir_attach(tdnode->ump, tdnode, fnode, &fvap, tcnp);
+       if (error)
+               return error;
+
+       /* unlink old directory entry for the node, if failing, unattach new */
+       error = udf_dir_detach(tdnode->ump, fdnode, fnode, fcnp);
+       if (error)
+               goto rollback_attach;
+
+       if ((fdnode != tdnode) && (fvp->v_type == VDIR)) {
+               /* update fnode's '..' entry */
+               error = udf_dir_update_rootentry(fnode->ump, fnode, tdnode);
+               if (error)
+                       goto rollback;
+       }
+
+       VN_KNOTE(fvp, NOTE_RENAME);
+       genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp);
+       return 0;
+
+rollback:
+       /* 'try' to recover from this situation */
+       udf_dir_attach(tdnode->ump, fdnode, fnode, &fvap, fcnp);
+rollback_attach:
+       udf_dir_detach(tdnode->ump, tdnode, fnode, tcnp);
+
+       return error;
+}
+
+
+/*
+ * udf_gro_remove: rename an object over another link to itself, effectively
+ * removing just the original link.
+ */
+static int
+udf_gro_remove(struct mount *mp, kauth_cred_t cred,
+    struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp)
+{
+       struct udf_node *dir_node, *udf_node;
+
+       KASSERT(mp != NULL);
+       KASSERT(dvp != NULL);
+       KASSERT(cnp != NULL);
+       KASSERT(vp != NULL);
+       KASSERT(dvp != vp);
+       KASSERT(dvp->v_mount == mp);
+       KASSERT(vp->v_mount == mp);
+       KASSERT(dvp->v_type == VDIR);
+       KASSERT(vp->v_type != VDIR);
+       KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+       KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+
+       DPRINTF(CALL, ("udf_gro_remove called\n"));
+
+       dir_node = VTOI(dvp);
+       udf_node = VTOI(vp);
+       udf_dir_detach(dir_node->ump, dir_node, udf_node, cnp);
+
+       return 0;
+}
+
+
+/*
+ * udf_gro_lookup: look up and save the lookup results.
+ */
+static int
+udf_gro_lookup(struct mount *mp, struct vnode *dvp,
+    struct componentname *cnp, void *de_ret, struct vnode **vp_ret)
+{
+       struct udf_node *dir_node, *res_node;
+       struct long_ad   icb_loc;
+       const char *name;
+       int namelen, error;
+       int found;
+
+       (void)mp;
+       KASSERT(mp != NULL);
+       KASSERT(dvp != NULL);
+       KASSERT(cnp != NULL);
+       KASSERT(vp_ret != NULL);
+       KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+
+       dir_node = VTOI(dvp);
+
+       DPRINTF(CALL, ("udf_gro_lookup called\n"));
+
+       /* lookup filename in the directory; location icb_loc */
+       name    = cnp->cn_nameptr;
+       namelen = cnp->cn_namelen;
+       error = udf_lookup_name_in_dir(dvp, name, namelen,
+                       &icb_loc, &found);
+       if (error)
+               return error;
+       if (!found)
+               return ENOENT;
+
+       DPRINTF(LOOKUP, ("udf_gro_lookup found '%s'\n", name));
+       error = udf_get_node(dir_node->ump, &icb_loc, &res_node);
+       if (error)
+               return error;
+       *vp_ret = res_node->vnode;
+       VOP_UNLOCK(res_node->vnode);
+
+       return 0;
+}
+
+
+/*
+ * udf_rmdired_p: check whether the directory vp has been rmdired.
+ *
+ * vp must be locked and referenced.
+ */
+static bool
+udf_rmdired_p(struct vnode *vp)
+{
+       DPRINTF(CALL, ("udf_rmdired_p called\n"));
+
+       KASSERT(vp != NULL);
+       KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+       KASSERT(vp->v_type == VDIR);
+
+       return (VTOI(vp)->i_flags & IN_DELETED);
+}
+
+
+/*
+ * udf_gro_genealogy: analyze the genealogy of the source and target
+ * directories.
+ */
+static int
+udf_gro_genealogy(struct mount *mp, kauth_cred_t cred,
+    struct vnode *fdvp, struct vnode *tdvp,
+    struct vnode **intermediate_node_ret)
+{
+       struct udf_mount *ump;
+       struct udf_node *parent_node;
+       struct vnode *vp, *dvp;
+       struct long_ad parent_loc;
+       const char *name;
+       int namelen;
+       int error, found;
+
+       (void)cred;
+       KASSERT(mp != NULL);
+       KASSERT(fdvp != NULL);
+       KASSERT(tdvp != NULL);
+       KASSERT(fdvp != tdvp);
+       KASSERT(intermediate_node_ret != NULL);
+       KASSERT(fdvp->v_mount == mp);
+       KASSERT(tdvp->v_mount == mp);
+       KASSERT(fdvp->v_type == VDIR);
+       KASSERT(tdvp->v_type == VDIR);
+
+       DPRINTF(CALL, ("udf_gro_genealogy called\n"));
+
+       /*
+        * We need to provisionally lock tdvp to keep rmdir from deleting it
+        * -- or any ancestor -- at an inopportune moment.
+        *
+        * XXX WHY is this not in genfs's rename? XXX
+        */
+       error = udf_gro_lock_directory(mp, tdvp);
+       if (error)
+               return error;
+
+       name     = "..";
+       namelen  = 2;
+       error    = 0;
+
+       ump = VTOI(tdvp)->ump;
+
+       /* if nodes are equal, it is no use looking */
+       KASSERT(udf_compare_icb(&VTOI(fdvp)->loc, &VTOI(tdvp)->loc) != 0);
+
+       /* start at destination vnode and walk up the tree */
+       vp = tdvp;
+       vref(vp);
+
+       for (;;) {
+               KASSERT(vp != NULL);
+               KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+               KASSERT(vp->v_mount == mp);
+               KASSERT(vp->v_type == VDIR);
+               KASSERT(!udf_rmdired_p(vp));
+
+               DPRINTF(NODE, ("udf_gro_genealogy : "
+                       "fdvp %p, looking at vp %p\n",
+                       fdvp, vp));
+
+               /* sanity check */
+               if (vp->v_type != VDIR) {
+                       vput(vp);
+                       return ENOTDIR;
+               }
+
+               /* go down one level */
+               error = udf_lookup_name_in_dir(vp, name, namelen,
+                       &parent_loc, &found);
+               DPRINTF(NODE, ("\tlookup of parent '..' resulted in error %d, "
+                       "found %d\n", error, found));
+               if (!found)
+                       error = ENOENT;
+               if (error) {
+                       vput(vp);
+                       return error;
+               }
+
+               /* did we encounter the root node? i.e. loop back */
+               if (udf_compare_icb(&parent_loc, &VTOI(vp)->loc) == 0) {
+                       DPRINTF(NODE, ("ROOT found!\n"));
+                       vput(vp);
+                       *intermediate_node_ret = NULL;
+                       return 0;
+               }
+
+               /* Did we find that fdvp is an ancestor of tdvp? */
+               if (udf_compare_icb(&parent_loc, &VTOI(fdvp)->loc) == 0) {
+                       DPRINTF(NODE, ("fdvp is ancestor of tdvp\n"));
+                       *intermediate_node_ret = vp;
+                       VOP_UNLOCK(vp);
+                       return 0;
+               }
+
+               /*
+                * Unlock vp so that we can lock the parent, but keep child vp
+                * referenced until after we have found the parent, so that
+                * parent_node will not be recycled.
+                */
+               DPRINTF(NODE, ("\tgetting the parent node\n"));
+               VOP_UNLOCK(vp);
+               error = udf_get_node(ump, &parent_loc, &parent_node);
+               vrele(vp);
+               if (error) 
+                       return error;
+
+               dvp = parent_node->vnode;
+
+               /* switch */
+               KASSERT(dvp != NULL);
+               KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+               vp  = dvp;
+
+               /* sanity check */
+               if (vp->v_type != VDIR) {
+                       /* 
+                        * Odd, but can happen if we loose the race and the
+                        * '..' node has been recycled.
+                        */
+                       vput(vp);
+                       return ENOTDIR;
+               }
+
+               if (udf_rmdired_p(vp)) {
+                       vput(vp);
+                       return ENOENT;
+               }
+       }
+}
+
+
+/*
+ * udf_gro_lock_directory: lock the directory vp, but fail if it has been
+ * rmdir'd.
+ */
+static int
+udf_gro_lock_directory(struct mount *mp, struct vnode *vp)
+{
+
+       (void)mp;
+       KASSERT(mp != NULL);
+       KASSERT(vp != NULL);
+       KASSERT(vp->v_mount == mp);
+
+       DPRINTF(CALL, ("udf_gro_lock_directory called\n"));
+       DPRINTF(LOCKING, ("udf_gro_lock_directory called\n"));
+
+       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+       if (udf_rmdired_p(vp)) {
+               VOP_UNLOCK(vp);
+               return ENOENT;
+       }
+
+       return 0;
+}
+
+
+static const struct genfs_rename_ops udf_genfs_rename_ops = {
+       .gro_directory_empty_p          = udf_gro_directory_empty_p,
+       .gro_rename_check_possible      = udf_gro_rename_check_possible,
+       .gro_rename_check_permitted     = udf_gro_rename_check_permitted,
+       .gro_remove_check_possible      = udf_gro_remove_check_possible,
+       .gro_remove_check_permitted     = udf_gro_remove_check_permitted,
+       .gro_rename                     = udf_gro_rename,
+       .gro_remove                     = udf_gro_remove,
+       .gro_lookup                     = udf_gro_lookup,
+       .gro_genealogy                  = udf_gro_genealogy,
+       .gro_lock_directory             = udf_gro_lock_directory,
+};
diff --git a/sys/fs/udf/udf_strat_bootstrap.c b/sys/fs/udf/udf_strat_bootstrap.c
new file mode 100644 (file)
index 0000000..74567ad
--- /dev/null
@@ -0,0 +1,145 @@
+/* $NetBSD: udf_strat_bootstrap.c,v 1.3 2008/12/16 16:18:25 pooka Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__KERNEL_RCSID(0, "$NetBSD: udf_strat_bootstrap.c,v 1.3 2008/12/16 16:18:25 pooka Exp $");
+#endif /* not lint */
+
+
+#if defined(_KERNEL_OPT)
+#include "opt_compat_netbsd.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#include "udf.h"
+#include "udf_subr.h"
+#include "udf_bswap.h"
+
+
+#define VTOI(vnode) ((struct udf_node *) vnode->v_data)
+#define PRIV(ump) ((struct strat_private *) ump->strategy_private)
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_create_logvol_dscr_bootstrap(struct udf_strat_args *args)
+{
+       panic("udf_create_logvol_dscr_bootstrap: not possible\n");
+       return 0;
+}
+
+
+static void
+udf_free_logvol_dscr_bootstrap(struct udf_strat_args *args)
+{
+       panic("udf_free_logvol_dscr_bootstrap: no node descriptor reading\n");
+}
+
+
+static int
+udf_read_logvol_dscr_bootstrap(struct udf_strat_args *args)
+{
+       panic("udf_read_logvol_dscr_bootstrap: no node descriptor reading\n");
+       return 0;
+}
+
+
+static int
+udf_write_logvol_dscr_bootstrap(struct udf_strat_args *args)
+{
+       panic("udf_write_logvol_dscr_bootstrap: no writing\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static void
+udf_queuebuf_bootstrap(struct udf_strat_args *args)
+{
+       struct udf_mount *ump = args->ump;
+       struct buf *buf = args->nestbuf;
+
+       KASSERT(ump);
+       KASSERT(buf);
+       KASSERT(buf->b_iodone == nestiobuf_iodone);
+
+       KASSERT(buf->b_flags & B_READ);
+       VOP_STRATEGY(ump->devvp, buf);
+}
+
+static void
+udf_discstrat_init_bootstrap(struct udf_strat_args *args)
+{
+       /* empty */
+}
+
+
+static void
+udf_discstrat_finish_bootstrap(struct udf_strat_args *args)
+{
+       /* empty */
+}
+
+/* --------------------------------------------------------------------- */
+
+struct udf_strategy udf_strat_bootstrap =
+{
+       udf_create_logvol_dscr_bootstrap,
+       udf_free_logvol_dscr_bootstrap,
+       udf_read_logvol_dscr_bootstrap,
+       udf_write_logvol_dscr_bootstrap,
+       udf_queuebuf_bootstrap,
+       udf_discstrat_init_bootstrap,
+       udf_discstrat_finish_bootstrap
+};
+
+
diff --git a/sys/fs/udf/udf_strat_direct.c b/sys/fs/udf/udf_strat_direct.c
new file mode 100644 (file)
index 0000000..c6cd591
--- /dev/null
@@ -0,0 +1,454 @@
+/* $NetBSD: udf_strat_direct.c,v 1.12 2013/10/30 08:41:38 mrg Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__KERNEL_RCSID(0, "$NetBSD: udf_strat_direct.c,v 1.12 2013/10/30 08:41:38 mrg Exp $");
+#endif /* not lint */
+
+
+#if defined(_KERNEL_OPT)
+#include "opt_compat_netbsd.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#include "udf.h"
+#include "udf_subr.h"
+#include "udf_bswap.h"
+
+
+#define VTOI(vnode) ((struct udf_node *) vnode->v_data)
+#define PRIV(ump) ((struct strat_private *) ump->strategy_private)
+
+/* --------------------------------------------------------------------- */
+
+/* BUFQ's */
+#define UDF_SHED_MAX 3
+
+#define UDF_SHED_READING       0
+#define UDF_SHED_WRITING       1
+#define UDF_SHED_SEQWRITING    2
+
+
+struct strat_private {
+       struct pool             desc_pool;       /* node descriptors */
+};
+
+/* --------------------------------------------------------------------- */
+
+static void
+udf_wr_nodedscr_callback(struct buf *buf)
+{
+       struct udf_node *udf_node;
+
+       KASSERT(buf);
+       KASSERT(buf->b_data);
+
+       /* called when write action is done */
+       DPRINTF(WRITE, ("udf_wr_nodedscr_callback(): node written out\n"));
+
+       udf_node = VTOI(buf->b_vp);
+       if (udf_node == NULL) {
+               putiobuf(buf);
+               printf("udf_wr_node_callback: NULL node?\n");
+               return;
+       }
+
+       /* XXX right flags to mark dirty again on error? */
+       if (buf->b_error) {
+               /* write error on `defect free' media??? how to solve? */
+               /* XXX lookup UDF standard for unallocatable space */
+               udf_node->i_flags |= IN_MODIFIED | IN_ACCESSED;
+       }
+
+       /* decrement outstanding_nodedscr */
+       KASSERT(udf_node->outstanding_nodedscr >= 1);
+       udf_node->outstanding_nodedscr--;
+       if (udf_node->outstanding_nodedscr == 0) {
+               /* unlock the node */
+               UDF_UNLOCK_NODE(udf_node, 0);
+               wakeup(&udf_node->outstanding_nodedscr);
+       }
+       /* unreference the vnode so it can be recycled */
+       holdrele(udf_node->vnode);
+
+       putiobuf(buf);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_getblank_nodedscr_direct(struct udf_strat_args *args)
+{
+       union dscrptr   **dscrptr = &args->dscr;
+       struct udf_mount *ump = args->ump;
+       struct strat_private *priv = PRIV(ump);
+       uint32_t lb_size;
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK);
+       memset(*dscrptr, 0, lb_size);
+
+       return 0;
+}
+
+
+static void
+udf_free_nodedscr_direct(struct udf_strat_args *args)
+{
+       union dscrptr    *dscr = args->dscr;
+       struct udf_mount *ump  = args->ump;
+       struct strat_private *priv = PRIV(ump);
+
+       pool_put(&priv->desc_pool, dscr);
+}
+
+
+static int
+udf_read_nodedscr_direct(struct udf_strat_args *args)
+{
+       union dscrptr   **dscrptr = &args->dscr;
+       union dscrptr    *tmpdscr;
+       struct udf_mount *ump = args->ump;
+       struct long_ad   *icb = args->icb;
+       struct strat_private *priv = PRIV(ump);
+       uint32_t lb_size;
+       uint32_t sector, dummy;
+       int error;
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+
+       error = udf_translate_vtop(ump, icb, &sector, &dummy);
+       if (error)
+               return error;
+
+       /* try to read in fe/efe */
+       error = udf_read_phys_dscr(ump, sector, M_UDFTEMP, &tmpdscr);
+       if (error)
+               return error;
+
+       *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK);
+       memcpy(*dscrptr, tmpdscr, lb_size);
+       free(tmpdscr, M_UDFTEMP);
+
+       return 0;
+}
+
+
+static int
+udf_write_nodedscr_direct(struct udf_strat_args *args)
+{
+       struct udf_mount *ump      = args->ump;
+       struct udf_node  *udf_node = args->udf_node;
+       union dscrptr    *dscr     = args->dscr;
+       struct long_ad   *icb      = args->icb;
+       int               waitfor  = args->waitfor;
+       uint32_t logsector, sector, dummy;
+       int error, vpart __diagused;
+
+       /*
+        * we have to decide if we write it out sequential or at its fixed 
+        * position by examining the partition its (to be) written on.
+        */
+       vpart     = udf_rw16(udf_node->loc.loc.part_num);
+       logsector = udf_rw32(icb->loc.lb_num);
+       KASSERT(ump->vtop_tp[vpart] != UDF_VTOP_TYPE_VIRT);
+
+       sector = 0;
+       error  = udf_translate_vtop(ump, icb, &sector, &dummy);
+       if (error)
+               goto out;
+
+       /* add reference to the vnode to prevent recycling */
+       vhold(udf_node->vnode);
+
+       if (waitfor) {
+               DPRINTF(WRITE, ("udf_write_nodedscr: sync write\n"));
+
+               error = udf_write_phys_dscr_sync(ump, udf_node, UDF_C_NODE,
+                       dscr, sector, logsector);
+       } else {
+               DPRINTF(WRITE, ("udf_write_nodedscr: no wait, async write\n"));
+
+               error = udf_write_phys_dscr_async(ump, udf_node, UDF_C_NODE,
+                       dscr, sector, logsector, udf_wr_nodedscr_callback);
+               /* will be UNLOCKED in call back */
+               return error;
+       }
+
+       holdrele(udf_node->vnode);
+out:
+       udf_node->outstanding_nodedscr--;
+       if (udf_node->outstanding_nodedscr == 0) {
+               UDF_UNLOCK_NODE(udf_node, 0);
+               wakeup(&udf_node->outstanding_nodedscr);
+       }
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void
+udf_queue_buf_direct(struct udf_strat_args *args)
+{
+       struct udf_mount *ump = args->ump;
+       struct buf *buf = args->nestbuf;
+       struct buf *nestbuf;
+       struct desc_tag *tag;
+       struct long_ad *node_ad_cpy;
+       uint64_t *lmapping, *pmapping, *lmappos, run_start;
+       uint32_t sectornr;
+       uint32_t buf_offset, rbuflen, bpos;
+       uint16_t vpart_num;
+       uint8_t *fidblk;
+       off_t rblk;
+       int sector_size = ump->discinfo.sector_size;
+       int len, buf_len, sector, sectors, run_length;
+       int blks = sector_size / DEV_BSIZE;
+       int what, class __diagused, queue;
+
+       KASSERT(ump);
+       KASSERT(buf);
+       KASSERT(buf->b_iodone == nestiobuf_iodone);
+
+       what = buf->b_udf_c_type;
+       queue = UDF_SHED_READING;
+       if ((buf->b_flags & B_READ) == 0) {
+               /* writing */
+               queue = UDF_SHED_SEQWRITING;
+               if (what == UDF_C_ABSOLUTE)
+                       queue = UDF_SHED_WRITING;
+               if (what == UDF_C_DSCR)
+                       queue = UDF_SHED_WRITING;
+               if (what == UDF_C_NODE)
+                       queue = UDF_SHED_WRITING;
+       }
+
+       /* use disc sheduler */
+       class = ump->discinfo.mmc_class;
+       KASSERT((class == MMC_CLASS_UNKN) || (class == MMC_CLASS_DISC) ||
+               (ump->discinfo.mmc_cur & MMC_CAP_HW_DEFECTFREE) ||
+               (ump->vfs_mountp->mnt_flag & MNT_RDONLY));
+
+#ifndef UDF_DEBUG
+       __USE(blks);
+#endif
+       if (queue == UDF_SHED_READING) {
+               DPRINTF(SHEDULE, ("\nudf_issue_buf READ %p : sector %d type %d,"
+                       "b_resid %d, b_bcount %d, b_bufsize %d\n",
+                       buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type,
+                       buf->b_resid, buf->b_bcount, buf->b_bufsize));
+               VOP_STRATEGY(ump->devvp, buf);
+               return;
+       }
+
+
+       if (queue == UDF_SHED_WRITING) {
+               DPRINTF(SHEDULE, ("\nudf_issue_buf WRITE %p : sector %d "
+                       "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n",
+                       buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type,
+                       buf->b_resid, buf->b_bcount, buf->b_bufsize));
+               KASSERT(buf->b_udf_c_type == UDF_C_DSCR || 
+                       buf->b_udf_c_type == UDF_C_ABSOLUTE ||
+                       buf->b_udf_c_type == UDF_C_NODE);
+                udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type);
+               VOP_STRATEGY(ump->devvp, buf);
+               return;
+       }
+
+       /* UDF_SHED_SEQWRITING */
+       KASSERT(queue == UDF_SHED_SEQWRITING);
+       DPRINTF(SHEDULE, ("\nudf_issue_buf SEQWRITE %p : sector XXXX "
+               "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n",
+               buf, buf->b_udf_c_type, buf->b_resid, buf->b_bcount,
+               buf->b_bufsize));
+
+       /*
+        * Buffers should not have been allocated to disc addresses yet on
+        * this queue. Note that a buffer can get multiple extents allocated.
+        *
+        * lmapping contains lb_num relative to base partition.
+        */
+       lmapping    = ump->la_lmapping;
+       node_ad_cpy = ump->la_node_ad_cpy;
+
+       /* logically allocate buf and map it in the file */
+       udf_late_allocate_buf(ump, buf, lmapping, node_ad_cpy, &vpart_num);
+
+       /* if we have FIDs, fixup using the new allocation table */
+       if (buf->b_udf_c_type == UDF_C_FIDS) {
+               buf_len = buf->b_bcount;
+               bpos = 0;
+               lmappos = lmapping;
+               while (buf_len) {
+                       sectornr = *lmappos++;
+                       len = MIN(buf_len, sector_size);
+                       fidblk = (uint8_t *) buf->b_data + bpos;
+                       udf_fixup_fid_block(fidblk, sector_size,
+                               0, len, sectornr);
+                       bpos += len;
+                       buf_len -= len;
+               }
+       }
+       if (buf->b_udf_c_type == UDF_C_METADATA_SBM) {
+               if (buf->b_lblkno == 0) {
+                       /* update the tag location inside */
+                       tag = (struct desc_tag *) buf->b_data;
+                       tag->tag_loc = udf_rw32(*lmapping);
+                       udf_validate_tag_and_crc_sums(buf->b_data);
+               }
+       }
+       udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type);
+
+       /*
+        * Translate new mappings in lmapping to pmappings and try to
+        * conglomerate extents to reduce the number of writes.
+        *
+        * pmapping to contain lb_nums as used for disc adressing.
+        */
+       pmapping = ump->la_pmapping;
+       sectors  = (buf->b_bcount + sector_size -1) / sector_size;
+       udf_translate_vtop_list(ump, sectors, vpart_num, lmapping, pmapping);
+
+       for (sector = 0; sector < sectors; sector++) {
+               buf_offset = sector * sector_size;
+               DPRINTF(WRITE, ("\tprocessing rel sector %d\n", sector));
+
+               DPRINTF(WRITE, ("\tissue write sector %"PRIu64"\n",
+                       pmapping[sector]));
+
+               run_start  = pmapping[sector];
+               run_length = 1;
+               while (sector < sectors-1) {
+                       if (pmapping[sector+1] != pmapping[sector]+1)
+                               break;
+                       run_length++;
+                       sector++;
+               }
+
+               /* nest an iobuf for the extent */
+               rbuflen = run_length *  sector_size;
+               rblk    = run_start  * (sector_size/DEV_BSIZE);
+
+               nestbuf = getiobuf(NULL, true);
+               nestiobuf_setup(buf, nestbuf, buf_offset, rbuflen);
+               /* nestbuf is B_ASYNC */
+
+               /* identify this nestbuf */
+               nestbuf->b_lblkno   = sector;
+               assert(nestbuf->b_vp == buf->b_vp);
+
+               /* CD shedules on raw blkno */
+               nestbuf->b_blkno      = rblk;
+               nestbuf->b_proc       = NULL;
+               nestbuf->b_rawblkno   = rblk;
+               nestbuf->b_udf_c_type = UDF_C_PROCESSED;
+
+               VOP_STRATEGY(ump->devvp, nestbuf);
+       }
+}
+
+
+static void
+udf_discstrat_init_direct(struct udf_strat_args *args)
+{
+       struct udf_mount  *ump = args->ump;
+       struct strat_private *priv = PRIV(ump);
+       uint32_t lb_size;
+
+       KASSERT(priv == NULL);
+       ump->strategy_private = malloc(sizeof(struct strat_private),
+               M_UDFTEMP, M_WAITOK);
+       priv = ump->strategy_private;
+       memset(priv, 0 , sizeof(struct strat_private));
+
+       /*
+        * Initialise pool for descriptors associated with nodes. This is done
+        * in lb_size units though currently lb_size is dictated to be
+        * sector_size.
+        */
+       memset(&priv->desc_pool, 0, sizeof(struct pool));
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       pool_init(&priv->desc_pool, lb_size, 0, 0, 0, "udf_desc_pool", NULL,
+           IPL_NONE);
+}
+
+
+static void
+udf_discstrat_finish_direct(struct udf_strat_args *args)
+{
+       struct udf_mount *ump = args->ump;
+       struct strat_private *priv = PRIV(ump);
+
+       /* destroy our pool */
+       pool_destroy(&priv->desc_pool);
+
+       /* free our private space */
+       free(ump->strategy_private, M_UDFTEMP);
+       ump->strategy_private = NULL;
+}
+
+/* --------------------------------------------------------------------- */
+
+struct udf_strategy udf_strat_direct =
+{
+       udf_getblank_nodedscr_direct,
+       udf_free_nodedscr_direct,
+       udf_read_nodedscr_direct,
+       udf_write_nodedscr_direct,
+       udf_queue_buf_direct,
+       udf_discstrat_init_direct,
+       udf_discstrat_finish_direct
+};
+
diff --git a/sys/fs/udf/udf_strat_rmw.c b/sys/fs/udf/udf_strat_rmw.c
new file mode 100644 (file)
index 0000000..cf04d81
--- /dev/null
@@ -0,0 +1,1507 @@
+/* $NetBSD: udf_strat_rmw.c,v 1.24 2013/10/30 08:41:38 mrg Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__KERNEL_RCSID(0, "$NetBSD: udf_strat_rmw.c,v 1.24 2013/10/30 08:41:38 mrg Exp $");
+#endif /* not lint */
+
+
+#if defined(_KERNEL_OPT)
+#include "opt_compat_netbsd.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#include "udf.h"
+#include "udf_subr.h"
+#include "udf_bswap.h"
+
+
+#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data)
+#define PRIV(ump) ((struct strat_private *) (ump)->strategy_private)
+#define BTOE(buf) ((struct udf_eccline *) ((buf)->b_private))
+
+/* --------------------------------------------------------------------- */
+
+#define UDF_MAX_PACKET_SIZE    64                      /* DONT change this */
+
+/* sheduler states */
+#define UDF_SHED_WAITING       1                       /* waiting on timeout */
+#define UDF_SHED_READING       2
+#define UDF_SHED_WRITING       3
+#define UDF_SHED_SEQWRITING    4
+#define UDF_SHED_IDLE          5                       /* refcnt'd */
+#define UDF_SHED_FREE          6                       /* recycleable */
+#define UDF_SHED_MAX           6+1
+
+/* flags */
+#define ECC_LOCKED             0x01                    /* prevent access   */
+#define ECC_WANTED             0x02                    /* trying access    */
+#define ECC_SEQWRITING         0x04                    /* sequential queue */
+#define ECC_FLOATING           0x08                    /* not queued yet   */
+
+#define ECC_WAITTIME           10
+
+
+TAILQ_HEAD(ecclineq, udf_eccline);
+struct udf_eccline {
+       struct udf_mount         *ump;
+       uint64_t                  present;              /* preserve these */
+       uint64_t                  readin;               /* bitmap */
+       uint64_t                  dirty;                /* bitmap */
+       uint64_t                  error;                /* bitmap */
+       uint32_t                  refcnt;
+
+       struct timespec           wait_time;
+       uint32_t                  flags;
+       uint32_t                  start_sector;         /* physical */
+
+       const char               *fname;
+       int                       sline;
+
+       struct buf               *buf;
+       void                     *blob;
+
+       struct buf               *bufs[UDF_MAX_PACKET_SIZE];
+       uint32_t                  bufs_bpos[UDF_MAX_PACKET_SIZE];
+       int                       bufs_len[UDF_MAX_PACKET_SIZE];
+
+       int                       queued_on;            /* on which BUFQ list */
+       LIST_ENTRY(udf_eccline)   hashchain;            /* on sector lookup  */
+};
+
+
+struct strat_private {
+       lwp_t                    *queue_lwp;
+       kcondvar_t                discstrat_cv;         /* to wait on       */
+       kmutex_t                  discstrat_mutex;      /* disc strategy    */
+       kmutex_t                  seqwrite_mutex;       /* protect mappings */
+
+       int                       thread_running;       /* thread control */
+       int                       run_thread;           /* thread control */
+       int                       thread_finished;      /* thread control */
+       int                       cur_queue;
+
+       int                       num_floating;
+       int                       num_queued[UDF_SHED_MAX];
+       struct bufq_state        *queues[UDF_SHED_MAX];
+       struct timespec           last_queued[UDF_SHED_MAX];
+       struct disk_strategy      old_strategy_setting;
+
+       struct pool               eccline_pool;
+       struct pool               ecclineblob_pool;
+       LIST_HEAD(, udf_eccline)  eccline_hash[UDF_ECCBUF_HASHSIZE];
+};
+
+/* --------------------------------------------------------------------- */
+
+#define UDF_LOCK_ECCLINE(eccline) udf_lock_eccline(eccline, __FILE__, __LINE__)
+#define UDF_UNLOCK_ECCLINE(eccline) udf_unlock_eccline(eccline, __FILE__, __LINE__)
+
+/* can be called with or without discstrat lock */
+static void
+udf_lock_eccline(struct udf_eccline *eccline, const char *fname, int sline)
+{
+       struct strat_private *priv = PRIV(eccline->ump);
+       int waslocked, ret;
+
+       KASSERT(mutex_owned(&priv->discstrat_mutex));
+
+       waslocked = mutex_owned(&priv->discstrat_mutex);
+       if (!waslocked)
+               mutex_enter(&priv->discstrat_mutex);
+
+       /* wait until its unlocked first */
+       eccline->refcnt++;
+       while (eccline->flags & ECC_LOCKED) {
+               DPRINTF(ECCLINE, ("waiting for lock at %s:%d\n",
+                                       fname, sline));
+               DPRINTF(ECCLINE, ("was locked at %s:%d\n",
+                                       eccline->fname, eccline->sline));
+               eccline->flags |= ECC_WANTED;
+               ret = cv_timedwait(&priv->discstrat_cv, &priv->discstrat_mutex,
+                       hz/8);
+               if (ret == EWOULDBLOCK)
+                       DPRINTF(LOCKING, ("eccline lock helt, waiting for "
+                               "release"));
+       }
+       eccline->flags |= ECC_LOCKED;
+       eccline->flags &= ~ECC_WANTED;
+       eccline->refcnt--;
+
+       eccline->fname = fname;
+       eccline->sline = sline;
+
+       if (!waslocked)
+               mutex_exit(&priv->discstrat_mutex);
+}
+
+
+/* can be called with or without discstrat lock */
+static void
+udf_unlock_eccline(struct udf_eccline *eccline, const char *fname, int sline)
+{
+       struct strat_private *priv = PRIV(eccline->ump);
+       int waslocked;
+
+       KASSERT(mutex_owned(&priv->discstrat_mutex));
+
+       waslocked = mutex_owned(&priv->discstrat_mutex);
+       if (!waslocked)
+               mutex_enter(&priv->discstrat_mutex);
+
+       eccline->flags &= ~ECC_LOCKED;
+       cv_broadcast(&priv->discstrat_cv);
+
+       if (!waslocked)
+               mutex_exit(&priv->discstrat_mutex);
+}
+
+
+/* NOTE discstrat_mutex should be held! */
+static void
+udf_dispose_eccline(struct udf_eccline *eccline)
+{
+       struct strat_private *priv = PRIV(eccline->ump);
+
+       KASSERT(mutex_owned(&priv->discstrat_mutex));
+
+       DPRINTF(ECCLINE, ("dispose eccline with start sector %d, "
+               "present %0"PRIx64"\n", eccline->start_sector,
+               eccline->present));
+
+       KASSERT(eccline->refcnt == 0);
+       KASSERT(eccline->dirty  == 0);
+       KASSERT(eccline->queued_on == 0);
+       KASSERT(eccline->flags & ECC_FLOATING);
+       KASSERT(eccline->flags & ECC_LOCKED);
+
+       LIST_REMOVE(eccline, hashchain);
+       priv->num_floating--;
+
+       putiobuf(eccline->buf);
+       pool_put(&priv->ecclineblob_pool, eccline->blob);
+       pool_put(&priv->eccline_pool, eccline);
+}
+
+
+/* NOTE discstrat_mutex should be held! */
+static void
+udf_push_eccline(struct udf_eccline *eccline, int newqueue)
+{
+       struct strat_private *priv = PRIV(eccline->ump);
+
+       KASSERT(mutex_owned(&priv->discstrat_mutex));
+
+       DPRINTF(PARANOIA, ("DEBUG: buf %p pushed on queue %d\n", eccline->buf, newqueue));
+
+       KASSERT(eccline->queued_on == 0);
+       KASSERT(eccline->flags & ECC_FLOATING);
+
+       /* set buffer block numbers to make sure its queued correctly */
+       eccline->buf->b_lblkno   = eccline->start_sector;
+       eccline->buf->b_blkno    = eccline->start_sector;
+       eccline->buf->b_rawblkno = eccline->start_sector;
+
+       vfs_timestamp(&priv->last_queued[newqueue]);
+       eccline->flags &= ~ECC_FLOATING;
+       priv->num_floating--;
+       eccline->queued_on = newqueue;
+       priv->num_queued[newqueue]++;
+       bufq_put(priv->queues[newqueue], eccline->buf);
+
+       UDF_UNLOCK_ECCLINE(eccline);
+
+       /* XXX tickle disc strategy statemachine */
+       if (newqueue != UDF_SHED_IDLE)
+               cv_signal(&priv->discstrat_cv);
+}
+
+
+static struct udf_eccline *
+udf_peek_eccline(struct strat_private *priv, int queued_on)
+{
+       struct udf_eccline *eccline;
+       struct buf *buf;
+
+       KASSERT(mutex_owned(&priv->discstrat_mutex));
+
+       for(;;) {
+               buf = bufq_peek(priv->queues[queued_on]);
+               /* could have been a race, but we'll revisit later */
+               if (buf == NULL)
+                       return NULL;
+
+               eccline = BTOE(buf);
+               UDF_LOCK_ECCLINE(eccline);
+
+               /* might have changed before we obtained the lock */
+               if (eccline->queued_on == queued_on)
+                       break;
+
+               UDF_UNLOCK_ECCLINE(eccline);
+       }
+
+       KASSERT(eccline->queued_on == queued_on);
+       KASSERT((eccline->flags & ECC_FLOATING) == 0);
+
+       DPRINTF(PARANOIA, ("DEBUG: buf %p peeked at queue %d\n",
+               eccline->buf, queued_on));
+
+       return eccline;
+}
+
+
+static struct udf_eccline *
+udf_pop_eccline(struct strat_private *priv, int queued_on)
+{
+       struct udf_eccline *eccline;
+       struct buf *buf;
+
+       KASSERT(mutex_owned(&priv->discstrat_mutex));
+
+       for(;;) {
+               buf = bufq_get(priv->queues[queued_on]);
+               if (buf == NULL) {
+                       // KASSERT(priv->num_queued[queued_on] == 0);
+                       return NULL;
+               }
+
+               eccline = BTOE(buf);
+               UDF_LOCK_ECCLINE(eccline);
+
+               /* might have changed before we obtained the lock */
+               if (eccline->queued_on == queued_on)
+                       break;
+
+               UDF_UNLOCK_ECCLINE(eccline);
+       }
+
+       KASSERT(eccline->queued_on == queued_on);
+       KASSERT((eccline->flags & ECC_FLOATING) == 0);
+
+       priv->num_queued[queued_on]--;
+       eccline->queued_on = 0;
+
+       eccline->flags |= ECC_FLOATING;
+       priv->num_floating++;
+
+       DPRINTF(PARANOIA, ("DEBUG: buf %p popped from queue %d\n",
+               eccline->buf, queued_on));
+
+       return eccline;
+}
+
+
+static void
+udf_unqueue_eccline(struct strat_private *priv, struct udf_eccline *eccline)
+{
+       struct buf *ret __diagused;
+
+       UDF_LOCK_ECCLINE(eccline);
+       if (eccline->queued_on == 0) {
+               KASSERT(eccline->flags & ECC_FLOATING);
+               return;
+       }
+
+       ret = bufq_cancel(priv->queues[eccline->queued_on], eccline->buf);
+       KASSERT(ret == eccline->buf);
+
+       priv->num_queued[eccline->queued_on]--;
+       eccline->queued_on = 0;
+
+       eccline->flags |= ECC_FLOATING;
+       priv->num_floating++;
+}
+
+
+static struct udf_eccline *
+udf_geteccline(struct udf_mount *ump, uint32_t sector, int flags)
+{
+       struct strat_private *priv = PRIV(ump);
+       struct udf_eccline *eccline;
+       uint32_t start_sector, lb_size, blobsize;
+       uint8_t *eccline_blob;
+       int line, line_offset;
+       int num_busy;
+
+       mutex_enter(&priv->discstrat_mutex);
+
+       /* lookup in our line cache hashtable */
+       line_offset  = sector % ump->packet_size;
+       start_sector = sector - line_offset;
+       line = (start_sector/ump->packet_size) & UDF_ECCBUF_HASHMASK;
+
+       KASSERT(priv->thread_running);
+
+retry:
+       DPRINTF(ECCLINE, ("get line sector %d, line %d\n", sector, line));
+       LIST_FOREACH(eccline, &priv->eccline_hash[line], hashchain) {
+               if (eccline->start_sector == start_sector) {
+                       DPRINTF(ECCLINE, ("\tfound eccline, start_sector %d\n",
+                               eccline->start_sector));
+                       udf_unqueue_eccline(priv, eccline);
+
+                       mutex_exit(&priv->discstrat_mutex);
+                       return eccline;
+               }
+       }
+
+       /* not found in eccline cache */
+       DPRINTF(ECCLINE, ("\tnot found in eccline cache\n"));
+
+       lb_size  = udf_rw32(ump->logical_vol->lb_size);
+       blobsize = ump->packet_size * lb_size;
+
+       /* dont allow too many pending requests */
+       DPRINTF(ECCLINE, ("\tallocating new eccline\n"));
+       num_busy = (priv->num_queued[UDF_SHED_SEQWRITING] + priv->num_floating);
+       if ((flags & ECC_SEQWRITING) && (num_busy > UDF_ECCLINE_MAXBUSY)) {
+               cv_timedwait(&priv->discstrat_cv,
+                       &priv->discstrat_mutex, hz/8);
+               goto retry;
+       }
+
+       eccline_blob = pool_get(&priv->ecclineblob_pool, PR_NOWAIT);
+       eccline = pool_get(&priv->eccline_pool, PR_NOWAIT);
+       if ((eccline_blob == NULL) || (eccline == NULL)) {
+               if (eccline_blob)
+                       pool_put(&priv->ecclineblob_pool, eccline_blob);
+               if (eccline)
+                       pool_put(&priv->eccline_pool, eccline);
+
+               /* out of memory for now; canibalise freelist */
+               eccline = udf_pop_eccline(priv, UDF_SHED_FREE);
+               if (eccline == NULL) {
+                       /* serious trouble; wait and retry */
+                       cv_timedwait(&priv->discstrat_cv,
+                               &priv->discstrat_mutex, hz/8);
+                       goto retry;
+               }
+
+               /* push back line if we're waiting for it or its locked */
+               if (eccline->flags & ECC_WANTED) {
+                       /* we won a race, but someone else needed it */
+                       udf_push_eccline(eccline, UDF_SHED_FREE);
+                       goto retry;
+               }
+
+               /* unlink this entry */
+               LIST_REMOVE(eccline, hashchain);
+               KASSERT(eccline->flags & ECC_FLOATING);
+               KASSERT(eccline->queued_on == 0);
+
+               eccline_blob = eccline->blob;
+               eccline->flags = ECC_FLOATING | ECC_LOCKED;
+       } else {
+               eccline->flags = ECC_FLOATING | ECC_LOCKED;
+               priv->num_floating++;
+       }
+
+       eccline->queued_on = 0;
+       eccline->blob = eccline_blob;
+       eccline->buf  = getiobuf(NULL, true);
+       eccline->buf->b_private = eccline;      /* IMPORTANT */
+
+       /* initialise eccline blob */
+       /* XXX memset expensive and strictly not needed XXX */
+       memset(eccline->blob, 0, blobsize);
+
+       eccline->ump = ump;
+       eccline->present = eccline->readin = eccline->dirty = 0;
+       eccline->error = 0;
+       eccline->refcnt = 0;
+       memset(eccline->bufs, 0, UDF_MAX_PACKET_SIZE * sizeof(struct buf *));
+
+       eccline->start_sector    = start_sector;
+       eccline->buf->b_lblkno   = start_sector;
+       eccline->buf->b_blkno    = start_sector;
+       eccline->buf->b_rawblkno = start_sector;
+
+       LIST_INSERT_HEAD(&priv->eccline_hash[line], eccline, hashchain);
+
+       /*
+        * TODO possible optimalisation for checking overlap with partitions
+        * to get a clue on future eccline usage
+        */
+
+       KASSERT(eccline->refcnt == 0);
+       KASSERT(eccline->flags & ECC_FLOATING);
+       KASSERT(eccline->flags & ECC_LOCKED);
+       mutex_exit(&priv->discstrat_mutex);
+
+       return eccline;
+}
+
+
+static void
+udf_puteccline(struct udf_eccline *eccline)
+{
+       struct strat_private *priv = PRIV(eccline->ump);
+       struct udf_mount *ump = eccline->ump;
+       uint64_t allbits = ((uint64_t) 1 << ump->packet_size)-1;
+       int new_queue;
+
+       mutex_enter(&priv->discstrat_mutex);
+
+       DPRINTF(ECCLINE, ("put eccline start sector %d, refcnt %d\n",
+               eccline->start_sector, eccline->refcnt));
+
+       KASSERT(eccline->flags & ECC_LOCKED);
+       KASSERT(eccline->flags & ECC_FLOATING);
+
+       /* clear all read bits that are already read in */
+       if (eccline->readin & eccline->present)
+               eccline->readin &= (~eccline->present) & allbits;
+
+       /* if we have active nodes we dont set it on seqwriting */
+       if (eccline->refcnt > 1)
+               eccline->flags &= ~ECC_SEQWRITING;
+
+       /* select state */
+       new_queue = UDF_SHED_FREE;
+       if (eccline->refcnt > 0)
+               new_queue = UDF_SHED_IDLE;
+       if (eccline->flags & ECC_WANTED)
+               new_queue = UDF_SHED_IDLE;
+       if (eccline->readin)
+               new_queue = UDF_SHED_READING;
+       if (eccline->dirty) {
+               new_queue = UDF_SHED_WAITING;
+               vfs_timestamp(&eccline->wait_time);
+               eccline->wait_time.tv_sec += ECC_WAITTIME;
+
+               if (eccline->present == allbits) {
+                       new_queue = UDF_SHED_WRITING;
+                       if (eccline->flags & ECC_SEQWRITING)
+                               new_queue = UDF_SHED_SEQWRITING;
+               }
+       }
+       udf_push_eccline(eccline, new_queue);
+
+       mutex_exit(&priv->discstrat_mutex);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_create_nodedscr_rmw(struct udf_strat_args *args)
+{
+       union dscrptr   **dscrptr  = &args->dscr;
+       struct udf_mount *ump      = args->ump;
+       struct long_ad   *icb      = args->icb;
+       struct udf_eccline *eccline;
+       uint64_t bit;
+       uint32_t sectornr, lb_size, dummy;
+       uint8_t *mem;
+       int error, eccsect;
+
+       error = udf_translate_vtop(ump, icb, &sectornr, &dummy);
+       if (error)
+               return error;
+
+       lb_size  = udf_rw32(ump->logical_vol->lb_size);
+
+       /* get our eccline */
+       eccline = udf_geteccline(ump, sectornr, 0);
+       eccsect = sectornr - eccline->start_sector;
+
+       bit = (uint64_t) 1 << eccsect;
+       eccline->readin  &= ~bit;       /* just in case */
+       eccline->present |=  bit;
+       eccline->dirty   &= ~bit;       /* Err... euhm... clean? */
+
+       eccline->refcnt++;
+
+       /* clear space */
+       mem = ((uint8_t *) eccline->blob) + eccsect * lb_size;
+       memset(mem, 0, lb_size);
+
+       udf_puteccline(eccline);
+
+       *dscrptr = (union dscrptr *) mem;
+       return 0;
+}
+
+
+static void
+udf_free_nodedscr_rmw(struct udf_strat_args *args)
+{
+       struct udf_mount *ump  = args->ump;
+       struct long_ad   *icb  = args->icb;
+       struct udf_eccline *eccline;
+       uint64_t bit;
+       uint32_t sectornr, dummy;
+       int error, eccsect;
+
+       error = udf_translate_vtop(ump, icb, &sectornr, &dummy);
+       if (error)
+               return;
+
+       /* get our eccline */
+       eccline = udf_geteccline(ump, sectornr, 0);
+       eccsect = sectornr - eccline->start_sector;
+
+       bit = (uint64_t) 1 << eccsect;
+       KASSERT(eccline->present & bit);
+
+       eccline->readin &= ~bit;        /* just in case */
+       /* XXX eccline->dirty? */
+
+       KASSERT(eccline->refcnt >= 1);
+       eccline->refcnt--;
+
+       udf_puteccline(eccline);
+}
+
+
+static int
+udf_read_nodedscr_rmw(struct udf_strat_args *args)
+{
+       union dscrptr   **dscrptr = &args->dscr;
+       struct udf_mount *ump = args->ump;
+       struct long_ad   *icb = args->icb;
+       struct strat_private *priv;
+       struct udf_eccline *eccline;
+       uint64_t bit;
+       uint32_t sectornr, dummy;
+       uint8_t *pos;
+       int sector_size = ump->discinfo.sector_size;
+       int lb_size = udf_rw32(ump->logical_vol->lb_size);
+       int i, error, dscrlen, eccsect;
+
+       lb_size = lb_size;
+       KASSERT(sector_size == lb_size);
+       error = udf_translate_vtop(ump, icb, &sectornr, &dummy);
+       if (error)
+               return error;
+
+       /* get our eccline */
+       eccline = udf_geteccline(ump, sectornr, 0);
+       eccsect = sectornr - eccline->start_sector;
+
+       bit = (uint64_t) 1 << eccsect;
+       if ((eccline->present & bit) == 0) {
+               /* mark bit for readin */
+               eccline->readin |= bit;
+               eccline->refcnt++;      /* prevent recycling */
+               KASSERT(eccline->bufs[eccsect] == NULL);
+               udf_puteccline(eccline);
+
+               /* wait for completion */
+               priv = PRIV(eccline->ump);
+               mutex_enter(&priv->discstrat_mutex);
+               while (((eccline->present | eccline->error) & bit) == 0) {
+                       error = cv_timedwait(&priv->discstrat_cv,
+                               &priv->discstrat_mutex,
+                               hz/8);
+                       if (error == EWOULDBLOCK)
+                               DPRINTF(LOCKING, ("eccline waiting for read\n"));
+               }
+               mutex_exit(&priv->discstrat_mutex);
+
+               /* reget our line */
+               eccline = udf_geteccline(ump, sectornr, 0);
+               KASSERT(eccline->refcnt >= 1);
+               eccline->refcnt--;      /* undo refcnt */
+
+               if (eccline->error & bit) {
+                       *dscrptr = NULL;
+                       udf_puteccline(eccline);
+                       return EIO;             /* XXX error code */
+               }
+       }
+
+       *dscrptr = (union dscrptr *)
+               (((uint8_t *) eccline->blob) + eccsect * sector_size);
+
+       /* code from read_phys_descr */
+       /* check if its a valid tag */
+       error = udf_check_tag(*dscrptr);
+       if (error) {
+               /* check if its an empty block */
+               pos = (uint8_t *) *dscrptr;
+               for (i = 0; i < sector_size; i++, pos++) {
+                       if (*pos) break;
+               }
+               if (i == sector_size) {
+                       /* return no error but with no dscrptr */
+                       error = 0;
+               }
+               *dscrptr = NULL;
+               udf_puteccline(eccline);
+               return error;
+       }
+
+       /* calculate descriptor size */
+       dscrlen = udf_tagsize(*dscrptr, sector_size);
+       error = udf_check_tag_payload(*dscrptr, dscrlen);
+       if (error) {
+               *dscrptr = NULL;
+               udf_puteccline(eccline);
+               return error;
+       }
+
+       /* we have a hold since it has a node descriptor */
+       eccline->refcnt++;
+       udf_puteccline(eccline);
+
+       return 0;
+}
+
+
+static int
+udf_write_nodedscr_rmw(struct udf_strat_args *args)
+{
+       union dscrptr    *dscrptr = args->dscr;
+       struct udf_mount *ump = args->ump;
+       struct long_ad   *icb = args->icb;
+       struct udf_node *udf_node = args->udf_node;
+       struct udf_eccline *eccline;
+       uint64_t bit;
+       uint32_t sectornr, logsectornr, dummy;
+       // int waitfor  = args->waitfor;
+       int sector_size = ump->discinfo.sector_size;
+       int lb_size = udf_rw32(ump->logical_vol->lb_size);
+       int error, eccsect;
+
+       lb_size = lb_size;
+       KASSERT(sector_size == lb_size);
+       sectornr    = 0;
+       error = udf_translate_vtop(ump, icb, &sectornr, &dummy);
+       if (error)
+               return error;
+
+       /* paranoia: add reference to the vnode to prevent recycling */
+       vhold(udf_node->vnode);
+
+       /* get our eccline */
+       eccline = udf_geteccline(ump, sectornr, 0);
+       eccsect = sectornr - eccline->start_sector;
+
+       bit = (uint64_t) 1 << eccsect;
+
+       /* old callback still pending? */
+       if (eccline->bufs[eccsect]) {
+               DPRINTF(WRITE, ("udf_write_nodedscr_rmw: writing descriptor"
+                                       " over buffer?\n"));
+               nestiobuf_done(eccline->bufs[eccsect],
+                               eccline->bufs_len[eccsect],
+                               0);
+               eccline->bufs[eccsect] = NULL;
+       }
+
+       /* set sector number in the descriptor and validate */
+       dscrptr = (union dscrptr *)
+               (((uint8_t *) eccline->blob) + eccsect * sector_size);
+       KASSERT(dscrptr == args->dscr);
+
+       logsectornr = udf_rw32(icb->loc.lb_num);
+       dscrptr->tag.tag_loc = udf_rw32(logsectornr);
+       udf_validate_tag_and_crc_sums(dscrptr);
+
+       udf_fixup_node_internals(ump, (uint8_t *) dscrptr, UDF_C_NODE);
+
+       /* set our flags */
+       KASSERT(eccline->present & bit);
+       eccline->dirty |= bit;
+
+       KASSERT(udf_tagsize(dscrptr, sector_size) <= sector_size);
+
+       udf_node->outstanding_nodedscr--;
+       if (udf_node->outstanding_nodedscr == 0) {
+               /* XXX still using wakeup! */
+               UDF_UNLOCK_NODE(udf_node, 0);
+               wakeup(&udf_node->outstanding_nodedscr);
+       }
+       holdrele(udf_node->vnode);
+       udf_puteccline(eccline);
+
+       /* XXX waitfor not used */
+       return 0;
+}
+
+
+static void
+udf_queuebuf_rmw(struct udf_strat_args *args)
+{
+       struct udf_mount *ump = args->ump;
+       struct buf *buf = args->nestbuf;
+       struct desc_tag *tag;
+       struct strat_private *priv = PRIV(ump);
+       struct udf_eccline *eccline;
+       struct long_ad *node_ad_cpy;
+       uint64_t bit, *lmapping, *pmapping, *lmappos, *pmappos, blknr;
+       uint32_t buf_len, len, sectors, sectornr, our_sectornr;
+       uint32_t bpos;
+       uint16_t vpart_num;
+       uint8_t *fidblk, *src, *dst;
+       int sector_size = ump->discinfo.sector_size;
+       int blks = sector_size / DEV_BSIZE;
+       int eccsect, what, queue, error;
+
+       KASSERT(ump);
+       KASSERT(buf);
+       KASSERT(buf->b_iodone == nestiobuf_iodone);
+
+       blknr        = buf->b_blkno;
+       our_sectornr = blknr / blks;
+
+       what = buf->b_udf_c_type;
+       queue = UDF_SHED_READING;
+       if ((buf->b_flags & B_READ) == 0) {
+               /* writing */
+               queue = UDF_SHED_SEQWRITING;
+               if (what == UDF_C_ABSOLUTE)
+                       queue = UDF_SHED_WRITING;
+               if (what == UDF_C_DSCR)
+                       queue = UDF_SHED_WRITING;
+               if (what == UDF_C_NODE)
+                       queue = UDF_SHED_WRITING;
+       }
+
+       if (queue == UDF_SHED_READING) {
+               DPRINTF(SHEDULE, ("\nudf_queuebuf_rmw READ %p : sector %d type %d,"
+                       "b_resid %d, b_bcount %d, b_bufsize %d\n",
+                       buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type,
+                       buf->b_resid, buf->b_bcount, buf->b_bufsize));
+
+               /* mark bits for reading */
+               buf_len = buf->b_bcount;
+               sectornr = our_sectornr;
+               eccline = udf_geteccline(ump, sectornr, 0);
+               eccsect = sectornr - eccline->start_sector;
+               bpos = 0;
+               while (buf_len) {
+                       len = MIN(buf_len, sector_size);
+                       if ((eccsect < 0) || (eccsect >= ump->packet_size)) {
+                               udf_puteccline(eccline);
+                               eccline = udf_geteccline(ump, sectornr, 0);
+                               eccsect = sectornr - eccline->start_sector;
+                       }
+                       bit = (uint64_t) 1 << eccsect;
+                       error = eccline->error & bit ? EIO : 0;
+                       if (eccline->present & bit) {
+                               src = (uint8_t *) eccline->blob + 
+                                       eccsect * sector_size;
+                               dst = (uint8_t *) buf->b_data + bpos;
+                               if (!error)
+                                       memcpy(dst, src, len);
+                               nestiobuf_done(buf, len, error);
+                       } else {
+                               eccline->readin |= bit;
+                               KASSERT(eccline->bufs[eccsect] == NULL);
+                               eccline->bufs[eccsect] = buf;
+                               eccline->bufs_bpos[eccsect] = bpos;
+                               eccline->bufs_len[eccsect] = len;
+                       }
+                       bpos += sector_size;
+                       eccsect++;
+                       sectornr++;
+                       buf_len -= len;
+               }
+               udf_puteccline(eccline);
+               return;
+       }
+
+       if (queue == UDF_SHED_WRITING) {
+               DPRINTF(SHEDULE, ("\nudf_queuebuf_rmw WRITE %p : sector %d "
+                       "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n",
+                       buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type,
+                       buf->b_resid, buf->b_bcount, buf->b_bufsize));
+
+               /* if we have FIDs fixup using buffer's sector number(s) */
+               if (buf->b_udf_c_type == UDF_C_FIDS)
+                       panic("UDF_C_FIDS in SHED_WRITING!\n");
+
+               udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type);
+
+               /* copy parts into the bufs and set for writing */
+               buf_len = buf->b_bcount;
+               sectornr = our_sectornr;
+               eccline = udf_geteccline(ump, sectornr, 0);
+               eccsect = sectornr - eccline->start_sector;
+               bpos = 0;
+               while (buf_len) {
+                       len = MIN(buf_len, sector_size);
+                       if ((eccsect < 0) || (eccsect >= ump->packet_size)) {
+                               udf_puteccline(eccline);
+                               eccline = udf_geteccline(ump, sectornr, 0);
+                               eccsect = sectornr - eccline->start_sector;
+                       }
+                       bit = (uint64_t) 1 << eccsect;
+                       KASSERT((eccline->readin & bit) == 0);
+                       eccline->present |= bit;
+                       eccline->dirty   |= bit;
+                       if (eccline->bufs[eccsect]) {
+                               /* old callback still pending */
+                               nestiobuf_done(eccline->bufs[eccsect],
+                                               eccline->bufs_len[eccsect],
+                                               0);
+                               eccline->bufs[eccsect] = NULL;
+                       }
+
+                       src = (uint8_t *) buf->b_data + bpos;
+                       dst = (uint8_t *) eccline->blob + eccsect * sector_size;
+                       if (len != sector_size)
+                               memset(dst, 0, sector_size);
+                       memcpy(dst, src, len);
+
+                       /* note that its finished for this extent */
+                       eccline->bufs[eccsect] = NULL;
+                       nestiobuf_done(buf, len, 0);
+
+                       bpos += sector_size;
+                       eccsect++;
+                       sectornr++;
+                       buf_len -= len;
+               }
+               udf_puteccline(eccline);
+               return;
+
+       }
+
+       /* sequential writing */
+       KASSERT(queue == UDF_SHED_SEQWRITING);
+       DPRINTF(SHEDULE, ("\nudf_queuebuf_rmw SEQWRITE %p : sector XXXX "
+               "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n",
+               buf, buf->b_udf_c_type, buf->b_resid, buf->b_bcount,
+               buf->b_bufsize));
+       /*
+        * Buffers should not have been allocated to disc addresses yet on
+        * this queue. Note that a buffer can get multiple extents allocated.
+        * Note that it *looks* like the normal writing but its different in
+        * the details.
+        *
+        * lmapping contains lb_num relative to base partition.
+        *
+        * XXX should we try to claim/organize the allocated memory to
+        * block-aligned pieces?
+        */
+       mutex_enter(&priv->seqwrite_mutex);
+
+       lmapping    = ump->la_lmapping;
+       node_ad_cpy = ump->la_node_ad_cpy;
+
+       /* logically allocate buf and map it in the file */
+       udf_late_allocate_buf(ump, buf, lmapping, node_ad_cpy, &vpart_num);
+
+       /* if we have FIDs, fixup using the new allocation table */
+       if (buf->b_udf_c_type == UDF_C_FIDS) {
+               buf_len = buf->b_bcount;
+               bpos = 0;
+               lmappos = lmapping;
+               while (buf_len) {
+                       sectornr = *lmappos++;
+                       len = MIN(buf_len, sector_size);
+                       fidblk = (uint8_t *) buf->b_data + bpos;
+                       udf_fixup_fid_block(fidblk, sector_size,
+                               0, len, sectornr);
+                       bpos += len;
+                       buf_len -= len;
+               }
+       }
+       if (buf->b_udf_c_type == UDF_C_METADATA_SBM) {
+               if (buf->b_lblkno == 0) {
+                       /* update the tag location inside */
+                       tag = (struct desc_tag *) buf->b_data;
+                       tag->tag_loc = udf_rw32(*lmapping);
+                       udf_validate_tag_and_crc_sums(buf->b_data);
+               }
+       }
+       udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type);
+
+       /*
+        * Translate new mappings in lmapping to pmappings.
+        * pmapping to contain lb_nums as used for disc adressing.
+        */
+       pmapping = ump->la_pmapping;
+       sectors  = (buf->b_bcount + sector_size -1) / sector_size;
+       udf_translate_vtop_list(ump, sectors, vpart_num, lmapping, pmapping);
+
+       /* copy parts into the bufs and set for writing */
+       pmappos = pmapping;
+       buf_len = buf->b_bcount;
+       sectornr = *pmappos++;
+       eccline = udf_geteccline(ump, sectornr, ECC_SEQWRITING);
+       eccsect = sectornr - eccline->start_sector;
+       bpos = 0;
+       while (buf_len) {
+               len = MIN(buf_len, sector_size);
+               eccsect = sectornr - eccline->start_sector;
+               if ((eccsect < 0) || (eccsect >= ump->packet_size)) {
+                       eccline->flags |= ECC_SEQWRITING;
+                       udf_puteccline(eccline);
+                       eccline = udf_geteccline(ump, sectornr, ECC_SEQWRITING);
+                       eccsect = sectornr - eccline->start_sector;
+               }
+               bit = (uint64_t) 1 << eccsect;
+               KASSERT((eccline->readin & bit) == 0);
+               eccline->present |= bit;
+               eccline->dirty   |= bit;
+               eccline->bufs[eccsect] = NULL;
+
+               src = (uint8_t *) buf->b_data + bpos;
+               dst = (uint8_t *)
+                       eccline->blob + eccsect * sector_size;
+               if (len != sector_size)
+                       memset(dst, 0, sector_size);
+               memcpy(dst, src, len);
+
+               /* note that its finished for this extent */
+               nestiobuf_done(buf, len, 0);
+
+               bpos += sector_size;
+               sectornr = *pmappos++;
+               buf_len -= len;
+       }
+       eccline->flags |= ECC_SEQWRITING;
+       udf_puteccline(eccline);
+       mutex_exit(&priv->seqwrite_mutex);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void 
+udf_shedule_read_callback(struct buf *buf)
+{
+       struct udf_eccline *eccline = BTOE(buf);
+       struct udf_mount *ump = eccline->ump;
+       uint64_t bit;
+       uint8_t *src, *dst;
+       int sector_size = ump->discinfo.sector_size;
+       int error, i, len;
+
+       DPRINTF(ECCLINE, ("read callback called on buf %p\n", buf));
+
+       /* post process read action */
+       KASSERT(eccline->flags & ECC_LOCKED);
+       error = buf->b_error;
+       for (i = 0; i < ump->packet_size; i++) {
+               bit = (uint64_t) 1 << i;
+               src = (uint8_t *) buf->b_data +   i * sector_size;
+               dst = (uint8_t *) eccline->blob + i * sector_size;
+               if (eccline->present & bit)
+                       continue;
+               eccline->present |= bit;
+               if (error)
+                       eccline->error |= bit;
+               if (eccline->bufs[i]) {
+                       dst = (uint8_t *) eccline->bufs[i]->b_data +
+                               eccline->bufs_bpos[i];
+                       len = eccline->bufs_len[i];
+                       if (!error)
+                               memcpy(dst, src, len);
+                       nestiobuf_done(eccline->bufs[i], len, error);
+                       eccline->bufs[i] = NULL;
+               }
+       
+       }
+       KASSERT(buf->b_data == eccline->blob);
+       KASSERT(eccline->present == ((uint64_t) 1 << ump->packet_size)-1);
+
+       /*
+        * XXX TODO what to do on read errors? read in all sectors
+        * synchronously and allocate a sparable entry?
+        */
+
+       udf_puteccline(eccline);
+       DPRINTF(ECCLINE, ("read callback finished\n"));
+}
+
+
+static void
+udf_shedule_write_callback(struct buf *buf)
+{
+       struct udf_eccline *eccline = BTOE(buf);
+       struct udf_mount *ump = eccline->ump;
+       uint64_t bit;
+       int error, i;
+
+       DPRINTF(ECCLINE, ("write callback called on buf %p\n", buf));
+
+       /* post process write action */
+       KASSERT(eccline->flags & ECC_LOCKED);
+       error = buf->b_error;
+       for (i = 0; i < ump->packet_size; i++) {
+               bit = (uint64_t) 1 << i;
+               if ((eccline->dirty & bit) == 0)
+                       continue;
+               if (error) {
+                       eccline->error |= bit;
+               } else {
+                       eccline->dirty &= ~bit;
+               }
+
+               KASSERT(eccline->bufs[i] == 0);
+       }
+       KASSERT(eccline->dirty == 0);
+       KASSERT(error == 0);
+
+       /*
+        * XXX TODO on write errors allocate a sparable entry and reissue
+        */
+
+       udf_puteccline(eccline);
+       DPRINTF(ECCLINE, ("write callback finished\n"));
+}
+
+
+static void
+udf_issue_eccline(struct udf_eccline *eccline, int queued_on)
+{
+       struct udf_mount *ump = eccline->ump;
+       struct strat_private *priv = PRIV(ump);
+       struct buf *buf, *nestbuf;
+       uint64_t bit, allbits = ((uint64_t) 1 << ump->packet_size)-1;
+       uint32_t start;
+       int sector_size = ump->discinfo.sector_size;
+       int blks = sector_size / DEV_BSIZE;
+       int i;
+
+       KASSERT(eccline->flags & ECC_LOCKED);
+
+       if (queued_on == UDF_SHED_READING) {
+               DPRINTF(SHEDULE, ("udf_issue_eccline reading : "));
+               /* read all bits that are not yet present */
+               eccline->readin = (~eccline->present) & allbits;
+               KASSERT(eccline->readin);
+               start = eccline->start_sector;
+               buf = eccline->buf;
+               buf->b_flags    = B_READ | B_ASYNC;
+               SET(buf->b_cflags, BC_BUSY);    /* mark buffer busy */
+               buf->b_oflags   = 0;
+               buf->b_iodone   = udf_shedule_read_callback;
+               buf->b_data     = eccline->blob;
+               buf->b_bcount   = ump->packet_size * sector_size;
+               buf->b_resid    = buf->b_bcount;
+               buf->b_bufsize  = buf->b_bcount;
+               buf->b_private  = eccline;
+               BIO_SETPRIO(buf, BPRIO_DEFAULT);
+               buf->b_lblkno   = buf->b_blkno = buf->b_rawblkno = start * blks;
+               buf->b_proc     = NULL;
+
+               if (eccline->present != 0) {
+                       for (i = 0; i < ump->packet_size; i++) {
+                               bit = (uint64_t) 1 << i;
+                               if (eccline->present & bit) {
+                                       nestiobuf_done(buf, sector_size, 0);
+                                       continue;
+                               }
+                               nestbuf = getiobuf(NULL, true);
+                               nestiobuf_setup(buf, nestbuf, i * sector_size,
+                                       sector_size);
+                               /* adjust blocknumber to read */
+                               nestbuf->b_blkno = buf->b_blkno + i*blks;
+                               nestbuf->b_rawblkno = buf->b_rawblkno + i*blks;
+
+                               DPRINTF(SHEDULE, ("sector %d ", start + i));
+
+                               /* mutex dance since it could lock */
+                               mutex_exit(&priv->discstrat_mutex);
+                                       /* call asynchronous */
+                                       VOP_STRATEGY(ump->devvp, nestbuf);
+                               mutex_enter(&priv->discstrat_mutex);
+                       }
+                       DPRINTF(SHEDULE, ("\n"));
+                       return;
+               }
+       } else {
+               /* write or seqwrite */
+               DPRINTF(SHEDULE, ("udf_issue_eccline writing or seqwriting : "));
+               DPRINTF(SHEDULE, ("\n\tpresent %"PRIx64", readin %"PRIx64", "
+                       "dirty %"PRIx64"\n\t", eccline->present, eccline->readin,
+                       eccline->dirty));
+               KASSERT(eccline->present == allbits);
+
+               start = eccline->start_sector;
+               buf = eccline->buf;
+               buf->b_flags    = B_WRITE | B_ASYNC;
+               SET(buf->b_cflags, BC_BUSY);    /* mark buffer busy */
+               buf->b_oflags   = 0;
+               buf->b_iodone   = udf_shedule_write_callback;
+               buf->b_data     = eccline->blob;
+               buf->b_bcount   = ump->packet_size * sector_size;
+               buf->b_resid    = buf->b_bcount;
+               buf->b_bufsize  = buf->b_bcount;
+               buf->b_private  = eccline;
+               BIO_SETPRIO(buf, BPRIO_DEFAULT);
+               buf->b_lblkno   = buf->b_blkno = buf->b_rawblkno = start * blks;
+               buf->b_proc     = NULL;
+       }
+
+       /* mutex dance since it could lock */
+       mutex_exit(&priv->discstrat_mutex);
+               /* call asynchronous */
+               DPRINTF(SHEDULE, ("sector %d for %d\n",
+                       start, ump->packet_size));
+               VOP_STRATEGY(ump->devvp, buf);
+       mutex_enter(&priv->discstrat_mutex);
+}
+
+
+static void
+udf_discstrat_thread(void *arg)
+{
+       struct udf_mount *ump = (struct udf_mount *) arg;
+       struct strat_private *priv = PRIV(ump);
+       struct udf_eccline *eccline;
+       struct timespec now, *last;
+       uint64_t allbits = ((uint64_t) 1 << ump->packet_size)-1;
+       int new_queue, wait, work;
+
+       work = 1;
+       priv->thread_running = 1;
+       mutex_enter(&priv->discstrat_mutex);
+       priv->num_floating = 0;
+       while (priv->run_thread || work || priv->num_floating) {
+               /* get our time */
+               vfs_timestamp(&now);
+
+               /* maintenance: handle eccline state machine */
+               for(;;) {
+                       /* only peek at it */
+                       eccline = udf_peek_eccline(priv, UDF_SHED_WAITING);
+                       if (eccline == NULL)
+                               break;
+
+                       /* if not reading, wait until the time has come */
+                       if ((priv->cur_queue != UDF_SHED_READING) &&
+                               (eccline->wait_time.tv_sec - now.tv_sec > 0)) {
+                                       UDF_UNLOCK_ECCLINE(eccline);
+                                       /* all others are later, so break off */
+                                       break;
+                       }
+
+                       /* release */
+                       UDF_UNLOCK_ECCLINE(eccline);
+
+                       /* do get it */
+                       eccline = udf_pop_eccline(priv, UDF_SHED_WAITING);
+
+                       /* requeue according to state */
+                       new_queue = UDF_SHED_FREE;      /* unlikely */
+                       if (eccline->refcnt > 0)
+                               new_queue = UDF_SHED_IDLE;
+                       if (eccline->flags & ECC_WANTED)
+                               new_queue = UDF_SHED_IDLE;
+                       if (eccline->readin)
+                               new_queue = UDF_SHED_READING;
+                       if (eccline->dirty) {
+                               new_queue = UDF_SHED_READING;
+                               if (eccline->present == allbits) {
+                                       new_queue = UDF_SHED_WRITING;
+                                       if (eccline->flags & ECC_SEQWRITING)
+                                               new_queue = UDF_SHED_SEQWRITING;
+                               }
+                       }
+                       udf_push_eccline(eccline, new_queue);
+               }
+
+               /* maintenance: free excess ecclines */
+               while (priv->num_queued[UDF_SHED_FREE] > UDF_ECCLINE_MAXFREE) {
+                       eccline = udf_pop_eccline(priv, UDF_SHED_FREE);
+                       KASSERT(eccline);
+                       KASSERT(eccline->refcnt == 0);
+                       if (eccline->flags & ECC_WANTED) {
+                               /* we won the race, but we dont want to win */
+                               DPRINTF(ECCLINE, ("Tried removing, pushed back to free list\n"));
+                               udf_push_eccline(eccline, UDF_SHED_IDLE);
+                       } else {
+                               DPRINTF(ECCLINE, ("Removing entry from free list\n"));
+                               udf_dispose_eccline(eccline);
+                       }
+               }
+
+               /* process the current selected queue */
+               /* get our time */
+               vfs_timestamp(&now);
+               last = &priv->last_queued[priv->cur_queue];
+
+               /* get our line */
+               eccline = udf_pop_eccline(priv, priv->cur_queue);
+               if (eccline) {
+                       wait = 0;
+                       new_queue = priv->cur_queue;
+                       DPRINTF(ECCLINE, ("UDF_ISSUE_ECCLINE\n"));
+
+                       udf_issue_eccline(eccline, priv->cur_queue);
+               } else {
+                       /* don't switch too quickly */
+                       if (now.tv_sec - last->tv_sec < 2) {
+                               /* wait some time */
+                               cv_timedwait(&priv->discstrat_cv,
+                                       &priv->discstrat_mutex, hz);
+                               /* we assume there is work to be done */
+                               work = 1;
+                               continue;
+                       }
+
+                       /* XXX select on queue lengths ? */
+                       wait = 1;
+                       /* check if we can/should switch */
+                       new_queue = priv->cur_queue;
+                       if (bufq_peek(priv->queues[UDF_SHED_READING]))
+                               new_queue = UDF_SHED_READING;
+                       if (bufq_peek(priv->queues[UDF_SHED_WRITING]))
+                               new_queue = UDF_SHED_WRITING;
+                       if (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]))
+                               new_queue = UDF_SHED_SEQWRITING;
+               }
+
+               /* give room */
+               mutex_exit(&priv->discstrat_mutex);
+
+               if (new_queue != priv->cur_queue) {
+                       wait = 0;
+                       DPRINTF(SHEDULE, ("switching from %d to %d\n",
+                               priv->cur_queue, new_queue));
+                       priv->cur_queue = new_queue;
+               }
+               mutex_enter(&priv->discstrat_mutex);
+
+               /* wait for more if needed */
+               if (wait)
+                       cv_timedwait(&priv->discstrat_cv,
+                               &priv->discstrat_mutex, hz/4);  /* /8 */
+
+               work  = (bufq_peek(priv->queues[UDF_SHED_WAITING]) != NULL);
+               work |= (bufq_peek(priv->queues[UDF_SHED_READING]) != NULL);
+               work |= (bufq_peek(priv->queues[UDF_SHED_WRITING]) != NULL);
+               work |= (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) != NULL);
+
+               DPRINTF(PARANOIA, ("work : (%d, %d, %d) -> work %d, float %d\n",
+                       (bufq_peek(priv->queues[UDF_SHED_READING]) != NULL),
+                       (bufq_peek(priv->queues[UDF_SHED_WRITING]) != NULL),
+                       (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) != NULL),
+                       work, priv->num_floating));
+       }
+
+       mutex_exit(&priv->discstrat_mutex);
+
+       /* tear down remaining ecclines */
+       mutex_enter(&priv->discstrat_mutex);
+       KASSERT(bufq_peek(priv->queues[UDF_SHED_WAITING]) == NULL);
+       KASSERT(bufq_peek(priv->queues[UDF_SHED_IDLE]) == NULL);
+       KASSERT(bufq_peek(priv->queues[UDF_SHED_READING]) == NULL);
+       KASSERT(bufq_peek(priv->queues[UDF_SHED_WRITING]) == NULL);
+       KASSERT(bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) == NULL);
+
+       KASSERT(priv->num_queued[UDF_SHED_WAITING] == 0);
+       KASSERT(priv->num_queued[UDF_SHED_IDLE] == 0);
+       KASSERT(priv->num_queued[UDF_SHED_READING] == 0);
+       KASSERT(priv->num_queued[UDF_SHED_WRITING] == 0);
+       KASSERT(priv->num_queued[UDF_SHED_SEQWRITING] == 0);
+
+       eccline = udf_pop_eccline(priv, UDF_SHED_FREE);
+       while (eccline) {
+               udf_dispose_eccline(eccline);
+               eccline = udf_pop_eccline(priv, UDF_SHED_FREE);
+       }
+       KASSERT(priv->num_queued[UDF_SHED_FREE] == 0);
+       mutex_exit(&priv->discstrat_mutex);
+
+       priv->thread_running  = 0;
+       priv->thread_finished = 1;
+       wakeup(&priv->run_thread);
+       kthread_exit(0);
+       /* not reached */
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Buffer memory pool allocator.
+ */
+
+static void *
+ecclinepool_page_alloc(struct pool *pp, int flags)
+{
+        return (void *)uvm_km_alloc(kernel_map,
+            MAXBSIZE, MAXBSIZE,
+            ((flags & PR_WAITOK) ? 0 : UVM_KMF_NOWAIT | UVM_KMF_TRYLOCK)
+               | UVM_KMF_WIRED /* UVM_KMF_PAGABLE? */);
+}
+
+static void
+ecclinepool_page_free(struct pool *pp, void *v)
+{
+        uvm_km_free(kernel_map, (vaddr_t)v, MAXBSIZE, UVM_KMF_WIRED);
+}
+
+static struct pool_allocator ecclinepool_allocator = {
+        .pa_alloc = ecclinepool_page_alloc,
+        .pa_free  = ecclinepool_page_free,
+        .pa_pagesz = MAXBSIZE,
+};
+
+
+static void
+udf_discstrat_init_rmw(struct udf_strat_args *args)
+{
+       struct udf_mount *ump = args->ump;
+       struct strat_private *priv = PRIV(ump);
+       uint32_t lb_size, blobsize, hashline;
+       int i;
+
+       KASSERT(ump);
+       KASSERT(ump->logical_vol);
+       KASSERT(priv == NULL);
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       blobsize = ump->packet_size * lb_size;
+       KASSERT(lb_size > 0);
+       KASSERT(ump->packet_size <= 64);
+
+       /* initialise our memory space */
+       ump->strategy_private = malloc(sizeof(struct strat_private),
+               M_UDFTEMP, M_WAITOK);
+       priv = ump->strategy_private;
+       memset(priv, 0 , sizeof(struct strat_private));
+
+       /* initialise locks */
+       cv_init(&priv->discstrat_cv, "udfstrat");
+       mutex_init(&priv->discstrat_mutex, MUTEX_DEFAULT, IPL_NONE);
+       mutex_init(&priv->seqwrite_mutex, MUTEX_DEFAULT, IPL_NONE);
+
+       /* initialise struct eccline pool */
+       pool_init(&priv->eccline_pool, sizeof(struct udf_eccline),
+               0, 0, 0, "udf_eccline_pool", NULL, IPL_NONE);
+
+       /* initialise eccline blob pool */
+        ecclinepool_allocator.pa_pagesz = blobsize;
+       pool_init(&priv->ecclineblob_pool, blobsize, 
+               0, 0, 0, "udf_eccline_blob", &ecclinepool_allocator, IPL_NONE);
+
+       /* initialise main queues */
+       for (i = 0; i < UDF_SHED_MAX; i++) {
+               priv->num_queued[i] = 0;
+               vfs_timestamp(&priv->last_queued[i]);
+       }
+       bufq_alloc(&priv->queues[UDF_SHED_WAITING], "fcfs",
+               BUFQ_SORT_RAWBLOCK);
+       bufq_alloc(&priv->queues[UDF_SHED_READING], "disksort",
+               BUFQ_SORT_RAWBLOCK);
+       bufq_alloc(&priv->queues[UDF_SHED_WRITING], "disksort",
+               BUFQ_SORT_RAWBLOCK);
+       bufq_alloc(&priv->queues[UDF_SHED_SEQWRITING], "disksort", 0);
+
+       /* initialise administrative queues */
+       bufq_alloc(&priv->queues[UDF_SHED_IDLE], "fcfs", 0);
+       bufq_alloc(&priv->queues[UDF_SHED_FREE], "fcfs", 0);
+
+       for (hashline = 0; hashline < UDF_ECCBUF_HASHSIZE; hashline++) {
+               LIST_INIT(&priv->eccline_hash[hashline]);
+       }
+
+       /* create our disk strategy thread */
+       priv->cur_queue = UDF_SHED_READING;
+       priv->thread_finished = 0;
+       priv->thread_running  = 0;
+       priv->run_thread      = 1;
+       if (kthread_create(PRI_NONE, 0 /* KTHREAD_MPSAFE*/, NULL /* cpu_info*/,
+               udf_discstrat_thread, ump, &priv->queue_lwp,
+               "%s", "udf_rw")) {
+               panic("fork udf_rw");
+       }
+
+       /* wait for thread to spin up */
+       while (!priv->thread_running) {
+               tsleep(&priv->thread_running, PRIBIO+1, "udfshedstart", hz);
+       }
+}
+
+
+static void
+udf_discstrat_finish_rmw(struct udf_strat_args *args)
+{
+       struct udf_mount *ump = args->ump;
+       struct strat_private *priv = PRIV(ump);
+
+       if (ump == NULL)
+               return;
+
+       /* stop our sheduling thread */
+       KASSERT(priv->run_thread == 1);
+       priv->run_thread = 0;
+       wakeup(priv->queue_lwp);
+       while (!priv->thread_finished) {
+               tsleep(&priv->run_thread, PRIBIO + 1, "udfshedfin", hz);
+       }
+       /* kthread should be finished now */
+
+       /* cleanup our pools */
+       pool_destroy(&priv->eccline_pool);
+       pool_destroy(&priv->ecclineblob_pool);
+
+       cv_destroy(&priv->discstrat_cv);
+       mutex_destroy(&priv->discstrat_mutex);
+       mutex_destroy(&priv->seqwrite_mutex);
+
+       /* free our private space */
+       free(ump->strategy_private, M_UDFTEMP);
+       ump->strategy_private = NULL;
+}
+
+/* --------------------------------------------------------------------- */
+
+struct udf_strategy udf_strat_rmw =
+{
+       udf_create_nodedscr_rmw,
+       udf_free_nodedscr_rmw,
+       udf_read_nodedscr_rmw,
+       udf_write_nodedscr_rmw,
+       udf_queuebuf_rmw,
+       udf_discstrat_init_rmw,
+       udf_discstrat_finish_rmw
+};
+
diff --git a/sys/fs/udf/udf_strat_sequential.c b/sys/fs/udf/udf_strat_sequential.c
new file mode 100644 (file)
index 0000000..0600b6a
--- /dev/null
@@ -0,0 +1,687 @@
+/* $NetBSD: udf_strat_sequential.c,v 1.12 2013/10/18 19:56:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__KERNEL_RCSID(0, "$NetBSD: udf_strat_sequential.c,v 1.12 2013/10/18 19:56:55 christos Exp $");
+#endif /* not lint */
+
+
+#if defined(_KERNEL_OPT)
+#include "opt_compat_netbsd.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#include "udf.h"
+#include "udf_subr.h"
+#include "udf_bswap.h"
+
+
+#define VTOI(vnode) ((struct udf_node *) vnode->v_data)
+#define PRIV(ump) ((struct strat_private *) ump->strategy_private)
+
+/* --------------------------------------------------------------------- */
+
+/* BUFQ's */
+#define UDF_SHED_MAX 3
+
+#define UDF_SHED_READING       0
+#define UDF_SHED_WRITING       1
+#define UDF_SHED_SEQWRITING    2
+
+struct strat_private {
+       struct pool              desc_pool;             /* node descriptors */
+
+       lwp_t                   *queue_lwp;
+       kcondvar_t               discstrat_cv;          /* to wait on       */
+       kmutex_t                 discstrat_mutex;       /* disc strategy    */
+
+       int                      run_thread;            /* thread control */
+       int                      cur_queue;
+
+       struct disk_strategy     old_strategy_setting;
+       struct bufq_state       *queues[UDF_SHED_MAX];
+       struct timespec          last_queued[UDF_SHED_MAX];
+};
+
+
+/* --------------------------------------------------------------------- */
+
+static void
+udf_wr_nodedscr_callback(struct buf *buf)
+{
+       struct udf_node *udf_node;
+
+       KASSERT(buf);
+       KASSERT(buf->b_data);
+
+       /* called when write action is done */
+       DPRINTF(WRITE, ("udf_wr_nodedscr_callback(): node written out\n"));
+
+       udf_node = VTOI(buf->b_vp);
+       if (udf_node == NULL) {
+               putiobuf(buf);
+               printf("udf_wr_node_callback: NULL node?\n");
+               return;
+       }
+
+       /* XXX right flags to mark dirty again on error? */
+       if (buf->b_error) {
+               udf_node->i_flags |= IN_MODIFIED | IN_ACCESSED;
+               /* XXX TODO reshedule on error */
+       }
+
+       /* decrement outstanding_nodedscr */
+       KASSERT(udf_node->outstanding_nodedscr >= 1);
+       udf_node->outstanding_nodedscr--;
+       if (udf_node->outstanding_nodedscr == 0) {
+               /* first unlock the node */
+               UDF_UNLOCK_NODE(udf_node, 0);
+               wakeup(&udf_node->outstanding_nodedscr);
+       }
+
+       /* unreference the vnode so it can be recycled */
+       holdrele(udf_node->vnode);
+
+       putiobuf(buf);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_create_logvol_dscr_seq(struct udf_strat_args *args)
+{
+       union dscrptr   **dscrptr = &args->dscr;
+       struct udf_mount *ump = args->ump;
+       struct strat_private *priv = PRIV(ump);
+       uint32_t lb_size;
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK);
+       memset(*dscrptr, 0, lb_size);
+
+       return 0;
+}
+
+
+static void
+udf_free_logvol_dscr_seq(struct udf_strat_args *args)
+{
+       union dscrptr    *dscr = args->dscr;
+       struct udf_mount *ump  = args->ump;
+       struct strat_private *priv = PRIV(ump);
+
+       pool_put(&priv->desc_pool, dscr);
+}
+
+
+static int
+udf_read_logvol_dscr_seq(struct udf_strat_args *args)
+{
+       union dscrptr   **dscrptr = &args->dscr;
+       union dscrptr    *tmpdscr;
+       struct udf_mount *ump = args->ump;
+       struct long_ad   *icb = args->icb;
+       struct strat_private *priv = PRIV(ump);
+       uint32_t lb_size;
+       uint32_t sector, dummy;
+       int error;
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+
+       error = udf_translate_vtop(ump, icb, &sector, &dummy);
+       if (error)
+               return error;
+
+       /* try to read in fe/efe */
+       error = udf_read_phys_dscr(ump, sector, M_UDFTEMP, &tmpdscr);
+       if (error)
+               return error;
+
+       *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK);
+       memcpy(*dscrptr, tmpdscr, lb_size);
+       free(tmpdscr, M_UDFTEMP);
+
+       return 0;
+}
+
+
+static int
+udf_write_logvol_dscr_seq(struct udf_strat_args *args)
+{
+       union dscrptr    *dscr     = args->dscr;
+       struct udf_mount *ump      = args->ump;
+       struct udf_node  *udf_node = args->udf_node;
+       struct long_ad   *icb      = args->icb;
+       int               waitfor  = args->waitfor;
+       uint32_t logsectornr, sectornr, dummy;
+       int error, vpart;
+
+       /*
+        * we have to decide if we write it out sequential or at its fixed 
+        * position by examining the partition its (to be) written on.
+        */
+       vpart       = udf_rw16(udf_node->loc.loc.part_num);
+       logsectornr = udf_rw32(icb->loc.lb_num);
+       sectornr    = 0;
+       if (ump->vtop_tp[vpart] != UDF_VTOP_TYPE_VIRT) {
+               error = udf_translate_vtop(ump, icb, &sectornr, &dummy);
+               if (error)
+                       goto out;
+       }
+
+       /* add reference to the vnode to prevent recycling */
+       vhold(udf_node->vnode);
+
+       if (waitfor) {
+               DPRINTF(WRITE, ("udf_write_logvol_dscr: sync write\n"));
+
+               error = udf_write_phys_dscr_sync(ump, udf_node, UDF_C_NODE,
+                       dscr, sectornr, logsectornr);
+       } else {
+               DPRINTF(WRITE, ("udf_write_logvol_dscr: no wait, async write\n"));
+
+               error = udf_write_phys_dscr_async(ump, udf_node, UDF_C_NODE,
+                       dscr, sectornr, logsectornr, udf_wr_nodedscr_callback);
+               /* will be UNLOCKED in call back */
+               return error;
+       }
+
+       holdrele(udf_node->vnode);
+out:
+       udf_node->outstanding_nodedscr--;
+       if (udf_node->outstanding_nodedscr == 0) {
+               UDF_UNLOCK_NODE(udf_node, 0);
+               wakeup(&udf_node->outstanding_nodedscr);
+       }
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Main file-system specific sheduler. Due to the nature of optical media
+ * sheduling can't be performed in the traditional way. Most OS
+ * implementations i've seen thus read or write a file atomically giving all
+ * kinds of side effects.
+ *
+ * This implementation uses a kernel thread to shedule the queued requests in
+ * such a way that is semi-optimal for optical media; this means aproximately
+ * (R*|(Wr*|Ws*))* since switching between reading and writing is expensive in
+ * time.
+ */
+
+static void
+udf_queuebuf_seq(struct udf_strat_args *args)
+{
+       struct udf_mount *ump = args->ump;
+       struct buf *nestbuf = args->nestbuf;
+       struct strat_private *priv = PRIV(ump);
+       int queue;
+       int what;
+
+       KASSERT(ump);
+       KASSERT(nestbuf);
+       KASSERT(nestbuf->b_iodone == nestiobuf_iodone);
+
+       what = nestbuf->b_udf_c_type;
+       queue = UDF_SHED_READING;
+       if ((nestbuf->b_flags & B_READ) == 0) {
+               /* writing */
+               queue = UDF_SHED_SEQWRITING;
+               if (what == UDF_C_ABSOLUTE)
+                       queue = UDF_SHED_WRITING;
+       }
+
+       /* use our own sheduler lists for more complex sheduling */
+       mutex_enter(&priv->discstrat_mutex);
+               bufq_put(priv->queues[queue], nestbuf);
+               vfs_timestamp(&priv->last_queued[queue]);
+       mutex_exit(&priv->discstrat_mutex);
+
+       /* signal our thread that there might be something to do */
+       cv_signal(&priv->discstrat_cv);
+}
+
+/* --------------------------------------------------------------------- */
+
+/* TODO convert to lb_size */
+static void
+udf_VAT_mapping_update(struct udf_mount *ump, struct buf *buf, uint32_t lb_map)
+{
+       union dscrptr    *fdscr = (union dscrptr *) buf->b_data;
+       struct vnode     *vp = buf->b_vp;
+       struct udf_node  *udf_node = VTOI(vp);
+       uint32_t lb_num;
+       uint32_t udf_rw32_lbmap;
+       int c_type = buf->b_udf_c_type;
+       int error;
+
+       /* only interested when we're using a VAT */
+       KASSERT(ump->vat_node);
+       KASSERT(ump->vtop_alloc[ump->node_part] == UDF_ALLOC_VAT);
+
+       /* only nodes are recorded in the VAT */
+       /* NOTE: and the fileset descriptor (FIXME ?) */
+       if (c_type != UDF_C_NODE)
+               return;
+
+       udf_rw32_lbmap = udf_rw32(lb_map);
+
+       /* if we're the VAT itself, only update our assigned sector number */
+       if (udf_node == ump->vat_node) {
+               fdscr->tag.tag_loc = udf_rw32_lbmap;
+               udf_validate_tag_sum(fdscr);
+               DPRINTF(TRANSLATE, ("VAT assigned to sector %u\n",
+                       udf_rw32(udf_rw32_lbmap)));
+               /* no use mapping the VAT node in the VAT */
+               return;
+       }
+
+       /* record new position in VAT file */
+       lb_num = udf_rw32(fdscr->tag.tag_loc);
+
+       /* lb_num = udf_rw32(udf_node->write_loc.loc.lb_num); */
+
+       DPRINTF(TRANSLATE, ("VAT entry change (log %u -> phys %u)\n",
+                       lb_num, lb_map));
+
+       /* VAT should be the longer than this write, can't go wrong */
+       KASSERT(lb_num <= ump->vat_entries);
+
+       mutex_enter(&ump->allocate_mutex);
+       error = udf_vat_write(ump->vat_node,
+                       (uint8_t *) &udf_rw32_lbmap, 4,
+                       ump->vat_offset + lb_num * 4);
+       mutex_exit(&ump->allocate_mutex);
+
+       if (error)
+               panic( "udf_VAT_mapping_update: HELP! i couldn't "
+                       "write in the VAT file ?\n");
+}
+
+
+static void
+udf_issue_buf(struct udf_mount *ump, int queue, struct buf *buf)
+{
+       union dscrptr *dscr;
+       struct long_ad *node_ad_cpy;
+       struct part_desc *pdesc;
+       uint64_t *lmapping, *lmappos;
+       uint32_t sectornr, bpos;
+       uint32_t ptov;
+       uint16_t vpart_num;
+       uint8_t *fidblk;
+       int sector_size = ump->discinfo.sector_size;
+       int blks = sector_size / DEV_BSIZE;
+       int len, buf_len;
+
+       /* if reading, just pass to the device's STRATEGY */
+       if (queue == UDF_SHED_READING) {
+               DPRINTF(SHEDULE, ("\nudf_issue_buf READ %p : sector %d type %d,"
+                       "b_resid %d, b_bcount %d, b_bufsize %d\n",
+                       buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type,
+                       buf->b_resid, buf->b_bcount, buf->b_bufsize));
+               VOP_STRATEGY(ump->devvp, buf);
+               return;
+       }
+
+       if (queue == UDF_SHED_WRITING) {
+               DPRINTF(SHEDULE, ("\nudf_issue_buf WRITE %p : sector %d "
+                       "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n",
+                       buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type,
+                       buf->b_resid, buf->b_bcount, buf->b_bufsize));
+               KASSERT(buf->b_udf_c_type == UDF_C_ABSOLUTE);
+
+               // udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type);
+               VOP_STRATEGY(ump->devvp, buf);
+               return;
+       }
+
+       KASSERT(queue == UDF_SHED_SEQWRITING);
+       DPRINTF(SHEDULE, ("\nudf_issue_buf SEQWRITE %p : sector XXXX "
+               "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n",
+               buf, buf->b_udf_c_type, buf->b_resid, buf->b_bcount,
+               buf->b_bufsize));
+
+       /*
+        * Buffers should not have been allocated to disc addresses yet on
+        * this queue. Note that a buffer can get multiple extents allocated.
+        *
+        * lmapping contains lb_num relative to base partition.
+        */
+       lmapping    = ump->la_lmapping;
+       node_ad_cpy = ump->la_node_ad_cpy;
+
+       /* logically allocate buf and map it in the file */
+       udf_late_allocate_buf(ump, buf, lmapping, node_ad_cpy, &vpart_num);
+
+       /*
+        * NOTE We are using the knowledge here that sequential media will
+        * always be mapped linearly. Thus no use to explicitly translate the
+        * lmapping list.
+        */
+
+       /* calculate offset from physical base partition */
+       pdesc = ump->partitions[ump->vtop[vpart_num]];
+       ptov  = udf_rw32(pdesc->start_loc);
+
+       /* set buffers blkno to the physical block number */
+       buf->b_blkno = (*lmapping + ptov) * blks;
+
+       /* fixate floating descriptors */
+       if (buf->b_udf_c_type == UDF_C_FLOAT_DSCR) {
+               /* set our tag location to the absolute position */
+               dscr = (union dscrptr *) buf->b_data;
+               dscr->tag.tag_loc = udf_rw32(*lmapping + ptov);
+               udf_validate_tag_and_crc_sums(dscr);
+       }
+
+       /* update mapping in the VAT */
+       if (buf->b_udf_c_type == UDF_C_NODE) {
+               udf_VAT_mapping_update(ump, buf, *lmapping);
+               udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type);
+       }
+
+       /* if we have FIDs, fixup using the new allocation table */
+       if (buf->b_udf_c_type == UDF_C_FIDS) {
+               buf_len = buf->b_bcount;
+               bpos = 0;
+               lmappos = lmapping;
+               while (buf_len) {
+                       sectornr = *lmappos++;
+                       len = MIN(buf_len, sector_size);
+                       fidblk = (uint8_t *) buf->b_data + bpos;
+                       udf_fixup_fid_block(fidblk, sector_size,
+                               0, len, sectornr);
+                       bpos += len;
+                       buf_len -= len;
+               }
+       }
+
+       VOP_STRATEGY(ump->devvp, buf);
+}
+
+
+static void
+udf_doshedule(struct udf_mount *ump)
+{
+       struct buf *buf;
+       struct timespec now, *last;
+       struct strat_private *priv = PRIV(ump);
+       void (*b_callback)(struct buf *);
+       int new_queue;
+       int error;
+
+       buf = bufq_get(priv->queues[priv->cur_queue]);
+       if (buf) {
+               /* transfer from the current queue to the device queue */
+               mutex_exit(&priv->discstrat_mutex);
+
+               /* transform buffer to synchronous; XXX needed? */
+               b_callback = buf->b_iodone;
+               buf->b_iodone = NULL;
+               CLR(buf->b_flags, B_ASYNC);
+
+               /* issue and wait on completion */
+               udf_issue_buf(ump, priv->cur_queue, buf);
+               biowait(buf);
+
+               mutex_enter(&priv->discstrat_mutex);
+
+               /* if there is an error, repair this error, otherwise propagate */
+               if (buf->b_error && ((buf->b_flags & B_READ) == 0)) {
+                       /* check what we need to do */
+                       panic("UDF write error, can't handle yet!\n");
+               }
+
+               /* propagate result to higher layers */
+               if (b_callback) {
+                       buf->b_iodone = b_callback;
+                       (*buf->b_iodone)(buf);
+               }
+
+               return;
+       }
+
+       /* Check if we're idling in this state */
+       vfs_timestamp(&now);
+       last = &priv->last_queued[priv->cur_queue];
+       if (ump->discinfo.mmc_class == MMC_CLASS_CD) {
+               /* dont switch too fast for CD media; its expensive in time */
+               if (now.tv_sec - last->tv_sec < 3)
+                       return;
+       }
+
+       /* check if we can/should switch */
+       new_queue = priv->cur_queue;
+
+       if (bufq_peek(priv->queues[UDF_SHED_READING]))
+               new_queue = UDF_SHED_READING;
+       if (bufq_peek(priv->queues[UDF_SHED_WRITING]))          /* only for unmount */
+               new_queue = UDF_SHED_WRITING;
+       if (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]))
+               new_queue = UDF_SHED_SEQWRITING;
+       if (priv->cur_queue == UDF_SHED_READING) {
+               if (new_queue == UDF_SHED_SEQWRITING) {
+                       /* TODO use flag to signal if this is needed */
+                       mutex_exit(&priv->discstrat_mutex);
+
+                       /* update trackinfo for data and metadata */
+                       error = udf_update_trackinfo(ump,
+                                       &ump->data_track);
+                       assert(error == 0);
+                       error = udf_update_trackinfo(ump,
+                                       &ump->metadata_track);
+                       assert(error == 0);
+                       mutex_enter(&priv->discstrat_mutex);
+               }
+       }
+
+       if (new_queue != priv->cur_queue) {
+               DPRINTF(SHEDULE, ("switching from %d to %d\n",
+                       priv->cur_queue, new_queue));
+       }
+
+       priv->cur_queue = new_queue;
+}
+
+
+static void
+udf_discstrat_thread(void *arg)
+{
+       struct udf_mount *ump = (struct udf_mount *) arg;
+       struct strat_private *priv = PRIV(ump);
+       int empty;
+
+       empty = 1;
+       mutex_enter(&priv->discstrat_mutex);
+       while (priv->run_thread || !empty) {
+               /* process the current selected queue */
+               udf_doshedule(ump);
+               empty  = (bufq_peek(priv->queues[UDF_SHED_READING]) == NULL);
+               empty &= (bufq_peek(priv->queues[UDF_SHED_WRITING]) == NULL);
+               empty &= (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) == NULL);
+
+               /* wait for more if needed */
+               if (empty)
+                       cv_timedwait(&priv->discstrat_cv,
+                               &priv->discstrat_mutex, hz/8);
+       }
+       mutex_exit(&priv->discstrat_mutex);
+
+       wakeup(&priv->run_thread);
+       kthread_exit(0);
+       /* not reached */
+}
+
+/* --------------------------------------------------------------------- */
+
+static void
+udf_discstrat_init_seq(struct udf_strat_args *args)
+{
+       struct udf_mount *ump = args->ump;
+       struct strat_private *priv = PRIV(ump);
+       struct disk_strategy dkstrat;
+       uint32_t lb_size;
+
+       KASSERT(ump);
+       KASSERT(ump->logical_vol);
+       KASSERT(priv == NULL);
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       KASSERT(lb_size > 0);
+
+       /* initialise our memory space */
+       ump->strategy_private = malloc(sizeof(struct strat_private),
+               M_UDFTEMP, M_WAITOK);
+       priv = ump->strategy_private;
+       memset(priv, 0 , sizeof(struct strat_private));
+
+       /* initialise locks */
+       cv_init(&priv->discstrat_cv, "udfstrat");
+       mutex_init(&priv->discstrat_mutex, MUTEX_DEFAULT, IPL_NONE);
+
+       /*
+        * Initialise pool for descriptors associated with nodes. This is done
+        * in lb_size units though currently lb_size is dictated to be
+        * sector_size.
+        */
+       pool_init(&priv->desc_pool, lb_size, 0, 0, 0, "udf_desc_pool", NULL,
+           IPL_NONE);
+
+       /*
+        * remember old device strategy method and explicit set method
+        * `discsort' since we have our own more complex strategy that is not
+        * implementable on the CD device and other strategies will get in the
+        * way.
+        */
+       memset(&priv->old_strategy_setting, 0,
+               sizeof(struct disk_strategy));
+       VOP_IOCTL(ump->devvp, DIOCGSTRATEGY, &priv->old_strategy_setting,
+               FREAD | FKIOCTL, NOCRED);
+       memset(&dkstrat, 0, sizeof(struct disk_strategy));
+       strcpy(dkstrat.dks_name, "discsort");
+       VOP_IOCTL(ump->devvp, DIOCSSTRATEGY, &dkstrat, FWRITE | FKIOCTL,
+               NOCRED);
+
+       /* initialise our internal sheduler */
+       priv->cur_queue = UDF_SHED_READING;
+       bufq_alloc(&priv->queues[UDF_SHED_READING], "disksort",
+               BUFQ_SORT_RAWBLOCK);
+       bufq_alloc(&priv->queues[UDF_SHED_WRITING], "disksort",
+               BUFQ_SORT_RAWBLOCK);
+       bufq_alloc(&priv->queues[UDF_SHED_SEQWRITING], "fcfs", 0);
+       vfs_timestamp(&priv->last_queued[UDF_SHED_READING]);
+       vfs_timestamp(&priv->last_queued[UDF_SHED_WRITING]);
+       vfs_timestamp(&priv->last_queued[UDF_SHED_SEQWRITING]);
+
+       /* create our disk strategy thread */
+       priv->run_thread = 1;
+       if (kthread_create(PRI_NONE, 0 /* KTHREAD_MPSAFE*/, NULL /* cpu_info*/,
+               udf_discstrat_thread, ump, &priv->queue_lwp,
+               "%s", "udf_rw")) {
+               panic("fork udf_rw");
+       }
+}
+
+
+static void
+udf_discstrat_finish_seq(struct udf_strat_args *args)
+{
+       struct udf_mount *ump = args->ump;
+       struct strat_private *priv = PRIV(ump);
+       int error;
+
+       if (ump == NULL)
+               return;
+
+       /* stop our sheduling thread */
+       KASSERT(priv->run_thread == 1);
+       priv->run_thread = 0;
+       wakeup(priv->queue_lwp);
+       do {
+               error = tsleep(&priv->run_thread, PRIBIO+1,
+                       "udfshedfin", hz);
+       } while (error);
+       /* kthread should be finished now */
+
+       /* set back old device strategy method */
+       VOP_IOCTL(ump->devvp, DIOCSSTRATEGY, &priv->old_strategy_setting,
+                       FWRITE, NOCRED);
+
+       /* destroy our pool */
+       pool_destroy(&priv->desc_pool);
+
+       mutex_destroy(&priv->discstrat_mutex);
+       cv_destroy(&priv->discstrat_cv);
+
+       /* free our private space */
+       free(ump->strategy_private, M_UDFTEMP);
+       ump->strategy_private = NULL;
+}
+
+/* --------------------------------------------------------------------- */
+
+struct udf_strategy udf_strat_sequential =
+{
+       udf_create_logvol_dscr_seq,
+       udf_free_logvol_dscr_seq,
+       udf_read_logvol_dscr_seq,
+       udf_write_logvol_dscr_seq,
+       udf_queuebuf_seq,
+       udf_discstrat_init_seq,
+       udf_discstrat_finish_seq
+};
+       
+
diff --git a/sys/fs/udf/udf_subr.c b/sys/fs/udf/udf_subr.c
new file mode 100644 (file)
index 0000000..83254af
--- /dev/null
@@ -0,0 +1,6817 @@
+/* $NetBSD: udf_subr.c,v 1.122 2013/11/21 23:42:09 riz Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+
+#include <sys/cdefs.h>
+#ifndef lint
+__KERNEL_RCSID(0, "$NetBSD: udf_subr.c,v 1.122 2013/11/21 23:42:09 riz Exp $");
+#endif /* not lint */
+
+
+#if defined(_KERNEL_OPT)
+#include "opt_compat_netbsd.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <fs/unicode.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+#include <sys/dirhash.h>
+
+#include "udf.h"
+#include "udf_subr.h"
+#include "udf_bswap.h"
+
+
+#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data)
+
+#define UDF_SET_SYSTEMFILE(vp) \
+       /* XXXAD Is the vnode locked? */        \
+       (vp)->v_vflag |= VV_SYSTEM;             \
+       vref((vp));                     \
+       vput((vp));                     \
+
+extern int syncer_maxdelay;     /* maximum delay time */
+extern int (**udf_vnodeop_p)(void *);
+
+/* --------------------------------------------------------------------- */
+
+//#ifdef DEBUG
+#if 1
+
+#if 0
+static void
+udf_dumpblob(boid *blob, uint32_t dlen)
+{
+       int i, j;
+
+       printf("blob = %p\n", blob);
+       printf("dump of %d bytes\n", dlen);
+
+       for (i = 0; i < dlen; i+ = 16) {
+               printf("%04x ", i);
+               for (j = 0; j < 16; j++) {
+                       if (i+j < dlen) {
+                               printf("%02x ", blob[i+j]);
+                       } else {
+                               printf("   ");
+                       }
+               }
+               for (j = 0; j < 16; j++) {
+                       if (i+j < dlen) {
+                               if (blob[i+j]>32 && blob[i+j]! = 127) {
+                                       printf("%c", blob[i+j]);
+                               } else {
+                                       printf(".");
+                               }
+                       }
+               }
+               printf("\n");
+       }
+       printf("\n");
+       Debugger();
+}
+#endif
+
+static void
+udf_dump_discinfo(struct udf_mount *ump)
+{
+       char   bits[128];
+       struct mmc_discinfo *di = &ump->discinfo;
+
+       if ((udf_verbose & UDF_DEBUG_VOLUMES) == 0)
+               return;
+
+       printf("Device/media info  :\n");
+       printf("\tMMC profile        0x%02x\n", di->mmc_profile);
+       printf("\tderived class      %d\n", di->mmc_class);
+       printf("\tsector size        %d\n", di->sector_size);
+       printf("\tdisc state         %d\n", di->disc_state);
+       printf("\tlast ses state     %d\n", di->last_session_state);
+       printf("\tbg format state    %d\n", di->bg_format_state);
+       printf("\tfrst track         %d\n", di->first_track);
+       printf("\tfst on last ses    %d\n", di->first_track_last_session);
+       printf("\tlst on last ses    %d\n", di->last_track_last_session);
+       printf("\tlink block penalty %d\n", di->link_block_penalty);
+       snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS, di->disc_flags);
+       printf("\tdisc flags         %s\n", bits);
+       printf("\tdisc id            %x\n", di->disc_id);
+       printf("\tdisc barcode       %"PRIx64"\n", di->disc_barcode);
+
+       printf("\tnum sessions       %d\n", di->num_sessions);
+       printf("\tnum tracks         %d\n", di->num_tracks);
+
+       snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur);
+       printf("\tcapabilities cur   %s\n", bits);
+       snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap);
+       printf("\tcapabilities cap   %s\n", bits);
+}
+
+static void
+udf_dump_trackinfo(struct mmc_trackinfo *trackinfo)
+{
+       char   bits[128];
+
+       if ((udf_verbose & UDF_DEBUG_VOLUMES) == 0)
+               return;
+
+       printf("Trackinfo for track %d:\n", trackinfo->tracknr);
+       printf("\tsessionnr           %d\n", trackinfo->sessionnr);
+       printf("\ttrack mode          %d\n", trackinfo->track_mode);
+       printf("\tdata mode           %d\n", trackinfo->data_mode);
+       snprintb(bits, sizeof(bits), MMC_TRACKINFO_FLAGBITS, trackinfo->flags);
+       printf("\tflags               %s\n", bits);
+
+       printf("\ttrack start         %d\n", trackinfo->track_start);
+       printf("\tnext_writable       %d\n", trackinfo->next_writable);
+       printf("\tfree_blocks         %d\n", trackinfo->free_blocks);
+       printf("\tpacket_size         %d\n", trackinfo->packet_size);
+       printf("\ttrack size          %d\n", trackinfo->track_size);
+       printf("\tlast recorded block %d\n", trackinfo->last_recorded);
+}
+
+#else
+#define udf_dump_discinfo(a);
+#define udf_dump_trackinfo(a);
+#endif
+
+
+/* --------------------------------------------------------------------- */
+
+/* not called often */
+int
+udf_update_discinfo(struct udf_mount *ump)
+{
+       struct vnode *devvp = ump->devvp;
+       uint64_t psize;
+       unsigned secsize;
+       struct mmc_discinfo *di;
+       int error;
+
+       DPRINTF(VOLUMES, ("read/update disc info\n"));
+       di = &ump->discinfo;
+       memset(di, 0, sizeof(struct mmc_discinfo));
+
+       /* check if we're on a MMC capable device, i.e. CD/DVD */
+       error = VOP_IOCTL(devvp, MMCGETDISCINFO, di, FKIOCTL, NOCRED);
+       if (error == 0) {
+               udf_dump_discinfo(ump);
+               return 0;
+       }
+
+       /* disc partition support */
+       error = getdisksize(devvp, &psize, &secsize);
+       if (error)
+               return error;
+
+       /* set up a disc info profile for partitions */
+       di->mmc_profile         = 0x01; /* disc type */
+       di->mmc_class           = MMC_CLASS_DISC;
+       di->disc_state          = MMC_STATE_CLOSED;
+       di->last_session_state  = MMC_STATE_CLOSED;
+       di->bg_format_state     = MMC_BGFSTATE_COMPLETED;
+       di->link_block_penalty  = 0;
+
+       di->mmc_cur     = MMC_CAP_RECORDABLE | MMC_CAP_REWRITABLE |
+               MMC_CAP_ZEROLINKBLK | MMC_CAP_HW_DEFECTFREE;
+       di->mmc_cap    = di->mmc_cur;
+       di->disc_flags = MMC_DFLAGS_UNRESTRICTED;
+
+       /* TODO problem with last_possible_lba on resizable VND; request */
+       di->last_possible_lba = psize;
+       di->sector_size       = secsize;
+
+       di->num_sessions = 1;
+       di->num_tracks   = 1;
+
+       di->first_track  = 1;
+       di->first_track_last_session = di->last_track_last_session = 1;
+
+       udf_dump_discinfo(ump);
+       return 0;
+}
+
+
+int
+udf_update_trackinfo(struct udf_mount *ump, struct mmc_trackinfo *ti)
+{
+       struct vnode *devvp = ump->devvp;
+       struct mmc_discinfo *di = &ump->discinfo;
+       int error, class;
+
+       DPRINTF(VOLUMES, ("read track info\n"));
+
+       class = di->mmc_class;
+       if (class != MMC_CLASS_DISC) {
+               /* tracknr specified in struct ti */
+               error = VOP_IOCTL(devvp, MMCGETTRACKINFO, ti, FKIOCTL, NOCRED);
+               return error;
+       }
+
+       /* disc partition support */
+       if (ti->tracknr != 1)
+               return EIO;
+
+       /* create fake ti (TODO check for resized vnds) */
+       ti->sessionnr  = 1;
+
+       ti->track_mode = 0;     /* XXX */
+       ti->data_mode  = 0;     /* XXX */
+       ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID;
+
+       ti->track_start    = 0;
+       ti->packet_size    = 1;
+
+       /* TODO support for resizable vnd */
+       ti->track_size    = di->last_possible_lba;
+       ti->next_writable = di->last_possible_lba;
+       ti->last_recorded = ti->next_writable;
+       ti->free_blocks   = 0;
+
+       return 0;
+}
+
+
+int
+udf_setup_writeparams(struct udf_mount *ump)
+{
+       struct mmc_writeparams mmc_writeparams;
+       int error;
+
+       if (ump->discinfo.mmc_class == MMC_CLASS_DISC)
+               return 0;
+
+       /*
+        * only CD burning normally needs setting up, but other disc types
+        * might need other settings to be made. The MMC framework will set up
+        * the nessisary recording parameters according to the disc
+        * characteristics read in. Modifications can be made in the discinfo
+        * structure passed to change the nature of the disc.
+        */
+
+       memset(&mmc_writeparams, 0, sizeof(struct mmc_writeparams));
+       mmc_writeparams.mmc_class  = ump->discinfo.mmc_class;
+       mmc_writeparams.mmc_cur    = ump->discinfo.mmc_cur;
+
+       /*
+        * UDF dictates first track to determine track mode for the whole
+        * disc. [UDF 1.50/6.10.1.1, UDF 1.50/6.10.2.1]
+        * To prevent problems with a `reserved' track in front we start with
+        * the 2nd track and if that is not valid, go for the 1st.
+        */
+       mmc_writeparams.tracknr = 2;
+       mmc_writeparams.data_mode  = MMC_DATAMODE_DEFAULT;      /* XA disc */
+       mmc_writeparams.track_mode = MMC_TRACKMODE_DEFAULT;     /* data */
+
+       error = VOP_IOCTL(ump->devvp, MMCSETUPWRITEPARAMS, &mmc_writeparams,
+                       FKIOCTL, NOCRED);
+       if (error) {
+               mmc_writeparams.tracknr = 1;
+               error = VOP_IOCTL(ump->devvp, MMCSETUPWRITEPARAMS,
+                               &mmc_writeparams, FKIOCTL, NOCRED);
+       }
+       return error;
+}
+
+
+int
+udf_synchronise_caches(struct udf_mount *ump)
+{
+       struct mmc_op mmc_op;
+
+       DPRINTF(CALL, ("udf_synchronise_caches()\n"));
+
+       if (ump->vfs_mountp->mnt_flag & MNT_RDONLY)
+               return 0;
+
+       /* discs are done now */
+       if (ump->discinfo.mmc_class == MMC_CLASS_DISC)
+               return 0;
+
+       memset(&mmc_op, 0, sizeof(struct mmc_op));
+       mmc_op.operation = MMC_OP_SYNCHRONISECACHE;
+
+       /* ignore return code */
+       (void) VOP_IOCTL(ump->devvp, MMCOP, &mmc_op, FKIOCTL, NOCRED);
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* track/session searching for mounting */
+int
+udf_search_tracks(struct udf_mount *ump, struct udf_args *args,
+                 int *first_tracknr, int *last_tracknr)
+{
+       struct mmc_trackinfo trackinfo;
+       uint32_t tracknr, start_track, num_tracks;
+       int error;
+
+       /* if negative, sessionnr is relative to last session */
+       if (args->sessionnr < 0) {
+               args->sessionnr += ump->discinfo.num_sessions;
+       }
+
+       /* sanity */
+       if (args->sessionnr < 0)
+               args->sessionnr = 0;
+       if (args->sessionnr > ump->discinfo.num_sessions)
+               args->sessionnr = ump->discinfo.num_sessions;
+
+       /* search the tracks for this session, zero session nr indicates last */
+       if (args->sessionnr == 0)
+               args->sessionnr = ump->discinfo.num_sessions;
+       if (ump->discinfo.last_session_state == MMC_STATE_EMPTY)
+               args->sessionnr--;
+
+       /* sanity again */
+       if (args->sessionnr < 0)
+               args->sessionnr = 0;
+
+       /* search the first and last track of the specified session */
+       num_tracks  = ump->discinfo.num_tracks;
+       start_track = ump->discinfo.first_track;
+
+       /* search for first track of this session */
+       for (tracknr = start_track; tracknr <= num_tracks; tracknr++) {
+               /* get track info */
+               trackinfo.tracknr = tracknr;
+               error = udf_update_trackinfo(ump, &trackinfo);
+               if (error)
+                       return error;
+
+               if (trackinfo.sessionnr == args->sessionnr)
+                       break;
+       }
+       *first_tracknr = tracknr;
+
+       /* search for last track of this session */
+       for (;tracknr <= num_tracks; tracknr++) {
+               /* get track info */
+               trackinfo.tracknr = tracknr;
+               error = udf_update_trackinfo(ump, &trackinfo);
+               if (error || (trackinfo.sessionnr != args->sessionnr)) {
+                       tracknr--;
+                       break;
+               }
+       }
+       if (tracknr > num_tracks)
+               tracknr--;
+
+       *last_tracknr = tracknr;
+
+       if (*last_tracknr < *first_tracknr) {
+               printf( "udf_search_tracks: sanity check on drive+disc failed, "
+                       "drive returned garbage\n");
+               return EINVAL;
+       }
+
+       assert(*last_tracknr >= *first_tracknr);
+       return 0;
+}
+
+
+/*
+ * NOTE: this is the only routine in this file that directly peeks into the
+ * metadata file but since its at a larval state of the mount it can't hurt.
+ *
+ * XXX candidate for udf_allocation.c
+ * XXX clean me up!, change to new node reading code.
+ */
+
+static void
+udf_check_track_metadata_overlap(struct udf_mount *ump,
+       struct mmc_trackinfo *trackinfo)
+{
+       struct part_desc *part;
+       struct file_entry      *fe;
+       struct extfile_entry   *efe;
+       struct short_ad        *s_ad;
+       struct long_ad         *l_ad;
+       uint32_t track_start, track_end;
+       uint32_t phys_part_start, phys_part_end, part_start, part_end;
+       uint32_t sector_size, len, alloclen, plb_num;
+       uint8_t *pos;
+       int addr_type, icblen, icbflags;
+
+       /* get our track extents */
+       track_start = trackinfo->track_start;
+       track_end   = track_start + trackinfo->track_size;
+
+       /* get our base partition extent */
+       KASSERT(ump->node_part == ump->fids_part);
+       part = ump->partitions[ump->vtop[ump->node_part]];
+       phys_part_start = udf_rw32(part->start_loc);
+       phys_part_end   = phys_part_start + udf_rw32(part->part_len);
+
+       /* no use if its outside the physical partition */
+       if ((phys_part_start >= track_end) || (phys_part_end < track_start))
+               return;
+
+       /*
+        * now follow all extents in the fe/efe to see if they refer to this
+        * track
+        */
+
+       sector_size = ump->discinfo.sector_size;
+
+       /* XXX should we claim exclusive access to the metafile ? */
+       /* TODO: move to new node read code */
+       fe  = ump->metadata_node->fe;
+       efe = ump->metadata_node->efe;
+       if (fe) {
+               alloclen = udf_rw32(fe->l_ad);
+               pos      = &fe->data[0] + udf_rw32(fe->l_ea);
+               icbflags = udf_rw16(fe->icbtag.flags);
+       } else {
+               assert(efe);
+               alloclen = udf_rw32(efe->l_ad);
+               pos      = &efe->data[0] + udf_rw32(efe->l_ea);
+               icbflags = udf_rw16(efe->icbtag.flags);
+       }
+       addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       while (alloclen) {
+               if (addr_type == UDF_ICB_SHORT_ALLOC) {
+                       icblen = sizeof(struct short_ad);
+                       s_ad   = (struct short_ad *) pos;
+                       len        = udf_rw32(s_ad->len);
+                       plb_num    = udf_rw32(s_ad->lb_num);
+               } else {
+                       /* should not be present, but why not */
+                       icblen = sizeof(struct long_ad);
+                       l_ad   = (struct long_ad *) pos;
+                       len        = udf_rw32(l_ad->len);
+                       plb_num    = udf_rw32(l_ad->loc.lb_num);
+                       /* pvpart_num = udf_rw16(l_ad->loc.part_num); */
+               }
+               /* process extent */
+               len     = UDF_EXT_LEN(len);
+
+               part_start = phys_part_start + plb_num;
+               part_end   = part_start + (len / sector_size);
+
+               if ((part_start >= track_start) && (part_end <= track_end)) {
+                       /* extent is enclosed within this track */
+                       ump->metadata_track = *trackinfo;
+                       return;
+               }
+
+               pos        += icblen;
+               alloclen   -= icblen;
+       }
+}
+
+
+int
+udf_search_writing_tracks(struct udf_mount *ump)
+{
+       struct vnode *devvp = ump->devvp;
+       struct mmc_trackinfo trackinfo;
+       struct mmc_op        mmc_op;
+       struct part_desc *part;
+       uint32_t tracknr, start_track, num_tracks;
+       uint32_t track_start, track_end, part_start, part_end;
+       int node_alloc, error;
+
+       /*
+        * in the CD/(HD)DVD/BD recordable device model a few tracks within
+        * the last session might be open but in the UDF device model at most
+        * three tracks can be open: a reserved track for delayed ISO VRS
+        * writing, a data track and a metadata track. We search here for the
+        * data track and the metadata track. Note that the reserved track is
+        * troublesome but can be detected by its small size of < 512 sectors.
+        */
+
+       /* update discinfo since it might have changed */
+       error = udf_update_discinfo(ump);
+       if (error)
+               return error;
+
+       num_tracks  = ump->discinfo.num_tracks;
+       start_track = ump->discinfo.first_track;
+
+       /* fetch info on first and possibly only track */
+       trackinfo.tracknr = start_track;
+       error = udf_update_trackinfo(ump, &trackinfo);
+       if (error)
+               return error;
+
+       /* copy results to our mount point */
+       ump->data_track     = trackinfo;
+       ump->metadata_track = trackinfo;
+
+       /* if not sequential, we're done */
+       if (num_tracks == 1)
+               return 0;
+
+       for (tracknr = start_track;tracknr <= num_tracks; tracknr++) {
+               /* get track info */
+               trackinfo.tracknr = tracknr;
+               error = udf_update_trackinfo(ump, &trackinfo);
+               if (error)
+                       return error;
+
+               /*
+                * If this track is marked damaged, ask for repair. This is an
+                * optional command, so ignore its error but report warning.
+                */
+               if (trackinfo.flags & MMC_TRACKINFO_DAMAGED) {
+                       memset(&mmc_op, 0, sizeof(mmc_op));
+                       mmc_op.operation   = MMC_OP_REPAIRTRACK;
+                       mmc_op.mmc_profile = ump->discinfo.mmc_profile;
+                       mmc_op.tracknr     = tracknr;
+                       error = VOP_IOCTL(devvp, MMCOP, &mmc_op, FKIOCTL, NOCRED);
+                       if (error)
+                               (void)printf("Drive can't explicitly repair "
+                                       "damaged track %d, but it might "
+                                       "autorepair\n", tracknr);
+
+                       /* reget track info */
+                       error = udf_update_trackinfo(ump, &trackinfo);
+                       if (error)
+                               return error;
+               }
+               if ((trackinfo.flags & MMC_TRACKINFO_NWA_VALID) == 0)
+                       continue;
+
+               track_start = trackinfo.track_start;
+               track_end   = track_start + trackinfo.track_size;
+
+               /* check for overlap on data partition */
+               part = ump->partitions[ump->data_part];
+               part_start = udf_rw32(part->start_loc);
+               part_end   = part_start + udf_rw32(part->part_len);
+               if ((part_start < track_end) && (part_end > track_start)) {
+                       ump->data_track = trackinfo;
+                       /* TODO check if UDF partition data_part is writable */
+               }
+
+               /* check for overlap on metadata partition */
+               node_alloc = ump->vtop_alloc[ump->node_part];
+               if ((node_alloc == UDF_ALLOC_METASEQUENTIAL) ||
+                   (node_alloc == UDF_ALLOC_METABITMAP)) {
+                       udf_check_track_metadata_overlap(ump, &trackinfo);
+               } else {
+                       ump->metadata_track = trackinfo;
+               }
+       }
+
+       if ((ump->data_track.flags & MMC_TRACKINFO_NWA_VALID) == 0)
+               return EROFS;
+
+       if ((ump->metadata_track.flags & MMC_TRACKINFO_NWA_VALID) == 0)
+               return EROFS;
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Check if the blob starts with a good UDF tag. Tags are protected by a
+ * checksum over the reader except one byte at position 4 that is the checksum
+ * itself.
+ */
+
+int
+udf_check_tag(void *blob)
+{
+       struct desc_tag *tag = blob;
+       uint8_t *pos, sum, cnt;
+
+       /* check TAG header checksum */
+       pos = (uint8_t *) tag;
+       sum = 0;
+
+       for(cnt = 0; cnt < 16; cnt++) {
+               if (cnt != 4)
+                       sum += *pos;
+               pos++;
+       }
+       if (sum != tag->cksum) {
+               /* bad tag header checksum; this is not a valid tag */
+               return EINVAL;
+       }
+
+       return 0;
+}
+
+
+/*
+ * check tag payload will check descriptor CRC as specified.
+ * If the descriptor is too long, it will return EIO otherwise EINVAL.
+ */
+
+int
+udf_check_tag_payload(void *blob, uint32_t max_length)
+{
+       struct desc_tag *tag = blob;
+       uint16_t crc, crc_len;
+
+       crc_len = udf_rw16(tag->desc_crc_len);
+
+       /* check payload CRC if applicable */
+       if (crc_len == 0)
+               return 0;
+
+       if (crc_len > max_length)
+               return EIO;
+
+       crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, crc_len);
+       if (crc != udf_rw16(tag->desc_crc)) {
+               /* bad payload CRC; this is a broken tag */
+               return EINVAL;
+       }
+
+       return 0;
+}
+
+
+void
+udf_validate_tag_sum(void *blob)
+{
+       struct desc_tag *tag = blob;
+       uint8_t *pos, sum, cnt;
+
+       /* calculate TAG header checksum */
+       pos = (uint8_t *) tag;
+       sum = 0;
+
+       for(cnt = 0; cnt < 16; cnt++) {
+               if (cnt != 4) sum += *pos;
+               pos++;
+       }
+       tag->cksum = sum;       /* 8 bit */
+}
+
+
+/* assumes sector number of descriptor to be saved already present */
+void
+udf_validate_tag_and_crc_sums(void *blob)
+{
+       struct desc_tag *tag  = blob;
+       uint8_t         *btag = (uint8_t *) tag;
+       uint16_t crc, crc_len;
+
+       crc_len = udf_rw16(tag->desc_crc_len);
+
+       /* check payload CRC if applicable */
+       if (crc_len > 0) {
+               crc = udf_cksum(btag + UDF_DESC_TAG_LENGTH, crc_len);
+               tag->desc_crc = udf_rw16(crc);
+       }
+
+       /* calculate TAG header checksum */
+       udf_validate_tag_sum(blob);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * XXX note the different semantics from udfclient: for FIDs it still rounds
+ * up to sectors. Use udf_fidsize() for a correct length.
+ */
+
+int
+udf_tagsize(union dscrptr *dscr, uint32_t lb_size)
+{
+       uint32_t size, tag_id, num_lb, elmsz;
+
+       tag_id = udf_rw16(dscr->tag.id);
+
+       switch (tag_id) {
+       case TAGID_LOGVOL :
+               size  = sizeof(struct logvol_desc) - 1;
+               size += udf_rw32(dscr->lvd.mt_l);
+               break;
+       case TAGID_UNALLOC_SPACE :
+               elmsz = sizeof(struct extent_ad);
+               size  = sizeof(struct unalloc_sp_desc) - elmsz;
+               size += udf_rw32(dscr->usd.alloc_desc_num) * elmsz;
+               break;
+       case TAGID_FID :
+               size = UDF_FID_SIZE + dscr->fid.l_fi + udf_rw16(dscr->fid.l_iu);
+               size = (size + 3) & ~3;
+               break;
+       case TAGID_LOGVOL_INTEGRITY :
+               size  = sizeof(struct logvol_int_desc) - sizeof(uint32_t);
+               size += udf_rw32(dscr->lvid.l_iu);
+               size += (2 * udf_rw32(dscr->lvid.num_part) * sizeof(uint32_t));
+               break;
+       case TAGID_SPACE_BITMAP :
+               size  = sizeof(struct space_bitmap_desc) - 1;
+               size += udf_rw32(dscr->sbd.num_bytes);
+               break;
+       case TAGID_SPARING_TABLE :
+               elmsz = sizeof(struct spare_map_entry);
+               size  = sizeof(struct udf_sparing_table) - elmsz;
+               size += udf_rw16(dscr->spt.rt_l) * elmsz;
+               break;
+       case TAGID_FENTRY :
+               size  = sizeof(struct file_entry);
+               size += udf_rw32(dscr->fe.l_ea) + udf_rw32(dscr->fe.l_ad)-1;
+               break;
+       case TAGID_EXTFENTRY :
+               size  = sizeof(struct extfile_entry);
+               size += udf_rw32(dscr->efe.l_ea) + udf_rw32(dscr->efe.l_ad)-1;
+               break;
+       case TAGID_FSD :
+               size  = sizeof(struct fileset_desc);
+               break;
+       default :
+               size = sizeof(union dscrptr);
+               break;
+       }
+
+       if ((size == 0) || (lb_size == 0))
+               return 0;
+
+       if (lb_size == 1)
+               return size;
+
+       /* round up in sectors */
+       num_lb = (size + lb_size -1) / lb_size;
+       return num_lb * lb_size;
+}
+
+
+int
+udf_fidsize(struct fileid_desc *fid)
+{
+       uint32_t size;
+
+       if (udf_rw16(fid->tag.id) != TAGID_FID)
+               panic("got udf_fidsize on non FID\n");
+
+       size = UDF_FID_SIZE + fid->l_fi + udf_rw16(fid->l_iu);
+       size = (size + 3) & ~3;
+
+       return size;
+}
+
+/* --------------------------------------------------------------------- */
+
+void
+udf_lock_node(struct udf_node *udf_node, int flag, char const *fname, const int lineno)
+{
+       int ret;
+
+       mutex_enter(&udf_node->node_mutex);
+       /* wait until free */
+       while (udf_node->i_flags & IN_LOCKED) {
+               ret = cv_timedwait(&udf_node->node_lock, &udf_node->node_mutex, hz/8);
+               /* TODO check if we should return error; abort */
+               if (ret == EWOULDBLOCK) {
+                       DPRINTF(LOCKING, ( "udf_lock_node: udf_node %p would block "
+                               "wanted at %s:%d, previously locked at %s:%d\n",
+                               udf_node, fname, lineno, 
+                               udf_node->lock_fname, udf_node->lock_lineno));
+               }
+       }
+       /* grab */
+       udf_node->i_flags |= IN_LOCKED | flag;
+       /* debug */
+       udf_node->lock_fname  = fname;
+       udf_node->lock_lineno = lineno;
+
+       mutex_exit(&udf_node->node_mutex);
+}
+
+
+void
+udf_unlock_node(struct udf_node *udf_node, int flag)
+{
+       mutex_enter(&udf_node->node_mutex);
+       udf_node->i_flags &= ~(IN_LOCKED | flag);
+       cv_broadcast(&udf_node->node_lock);
+       mutex_exit(&udf_node->node_mutex);
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_read_anchor(struct udf_mount *ump, uint32_t sector, struct anchor_vdp **dst)
+{
+       int error;
+
+       error = udf_read_phys_dscr(ump, sector, M_UDFVOLD,
+                       (union dscrptr **) dst);
+       if (!error) {
+               /* blank terminator blocks are not allowed here */
+               if (*dst == NULL)
+                       return ENOENT;
+               if (udf_rw16((*dst)->tag.id) != TAGID_ANCHOR) {
+                       error = ENOENT;
+                       free(*dst, M_UDFVOLD);
+                       *dst = NULL;
+                       DPRINTF(VOLUMES, ("Not an anchor\n"));
+               }
+       }
+
+       return error;
+}
+
+
+int
+udf_read_anchors(struct udf_mount *ump)
+{
+       struct udf_args *args = &ump->mount_args;
+       struct mmc_trackinfo first_track;
+       struct mmc_trackinfo second_track;
+       struct mmc_trackinfo last_track;
+       struct anchor_vdp **anchorsp;
+       uint32_t track_start;
+       uint32_t track_end;
+       uint32_t positions[4];
+       int first_tracknr, last_tracknr;
+       int error, anch, ok, first_anchor;
+
+       /* search the first and last track of the specified session */
+       error = udf_search_tracks(ump, args, &first_tracknr, &last_tracknr);
+       if (!error) {
+               first_track.tracknr = first_tracknr;
+               error = udf_update_trackinfo(ump, &first_track);
+       }
+       if (!error) {
+               last_track.tracknr = last_tracknr;
+               error = udf_update_trackinfo(ump, &last_track);
+       }
+       if ((!error) && (first_tracknr != last_tracknr)) {
+               second_track.tracknr = first_tracknr+1;
+               error = udf_update_trackinfo(ump, &second_track);
+       }
+       if (error) {
+               printf("UDF mount: reading disc geometry failed\n");
+               return 0;
+       }
+
+       track_start = first_track.track_start;
+
+       /* `end' is not as straitforward as start. */
+       track_end =   last_track.track_start
+                   + last_track.track_size - last_track.free_blocks - 1;
+
+       if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) {
+               /* end of track is not straitforward here */
+               if (last_track.flags & MMC_TRACKINFO_LRA_VALID)
+                       track_end = last_track.last_recorded;
+               else if (last_track.flags & MMC_TRACKINFO_NWA_VALID)
+                       track_end = last_track.next_writable
+                                   - ump->discinfo.link_block_penalty;
+       }
+
+       /* its no use reading a blank track */
+       first_anchor = 0;
+       if (first_track.flags & MMC_TRACKINFO_BLANK)
+               first_anchor = 1;
+
+       /* get our packet size */
+       ump->packet_size = first_track.packet_size;
+       if (first_track.flags & MMC_TRACKINFO_BLANK)
+               ump->packet_size = second_track.packet_size;
+
+       if (ump->packet_size <= 1) {
+               /* take max, but not bigger than 64 */
+               ump->packet_size = MAXPHYS / ump->discinfo.sector_size;
+               ump->packet_size = MIN(ump->packet_size, 64);
+       }
+       KASSERT(ump->packet_size >= 1);
+
+       /* read anchors start+256, start+512, end-256, end */
+       positions[0] = track_start+256;
+       positions[1] =   track_end-256;
+       positions[2] =   track_end;
+       positions[3] = track_start+512; /* [UDF 2.60/6.11.2] */
+       /* XXX shouldn't +512 be prefered above +256 for compat with Roxio CD */
+
+       ok = 0;
+       anchorsp = ump->anchors;
+       for (anch = first_anchor; anch < 4; anch++) {
+               DPRINTF(VOLUMES, ("Read anchor %d at sector %d\n", anch,
+                   positions[anch]));
+               error = udf_read_anchor(ump, positions[anch], anchorsp);
+               if (!error) {
+                       anchorsp++;
+                       ok++;
+               }
+       }
+
+       /* VATs are only recorded on sequential media, but initialise */
+       ump->first_possible_vat_location = track_start + 2;
+       ump->last_possible_vat_location  = track_end + last_track.packet_size;
+
+       return ok;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_get_c_type(struct udf_node *udf_node)
+{
+       int isdir, what;
+
+       isdir  = (udf_node->vnode->v_type == VDIR);
+       what   = isdir ? UDF_C_FIDS : UDF_C_USERDATA;
+
+       if (udf_node->ump)
+               if (udf_node == udf_node->ump->metadatabitmap_node)
+                       what = UDF_C_METADATA_SBM;
+
+       return what;
+}
+
+
+int
+udf_get_record_vpart(struct udf_mount *ump, int udf_c_type)
+{
+       int vpart_num;
+
+       vpart_num = ump->data_part;
+       if (udf_c_type == UDF_C_NODE)
+               vpart_num = ump->node_part;
+       if (udf_c_type == UDF_C_FIDS)
+               vpart_num = ump->fids_part;
+
+       return vpart_num;
+}
+
+
+/* 
+ * BUGALERT: some rogue implementations use random physical partition
+ * numbers to break other implementations so lookup the number.
+ */
+
+static uint16_t
+udf_find_raw_phys(struct udf_mount *ump, uint16_t raw_phys_part)
+{
+       struct part_desc *part;
+       uint16_t phys_part;
+
+       for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) {
+               part = ump->partitions[phys_part];
+               if (part == NULL)
+                       break;
+               if (udf_rw16(part->part_num) == raw_phys_part)
+                       break;
+       }
+       return phys_part;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* we dont try to be smart; we just record the parts */
+#define UDF_UPDATE_DSCR(name, dscr) \
+       if (name) \
+               free(name, M_UDFVOLD); \
+       name = dscr;
+
+static int
+udf_process_vds_descriptor(struct udf_mount *ump, union dscrptr *dscr)
+{
+       uint16_t phys_part, raw_phys_part;
+
+       DPRINTF(VOLUMES, ("\tprocessing VDS descr %d\n",
+           udf_rw16(dscr->tag.id)));
+       switch (udf_rw16(dscr->tag.id)) {
+       case TAGID_PRI_VOL :            /* primary partition            */
+               UDF_UPDATE_DSCR(ump->primary_vol, &dscr->pvd);
+               break;
+       case TAGID_LOGVOL :             /* logical volume               */
+               UDF_UPDATE_DSCR(ump->logical_vol, &dscr->lvd);
+               break;
+       case TAGID_UNALLOC_SPACE :      /* unallocated space            */
+               UDF_UPDATE_DSCR(ump->unallocated, &dscr->usd);
+               break;
+       case TAGID_IMP_VOL :            /* implementation               */
+               /* XXX do we care about multiple impl. descr ? */
+               UDF_UPDATE_DSCR(ump->implementation, &dscr->ivd);
+               break;
+       case TAGID_PARTITION :          /* physical partition           */
+               /* not much use if its not allocated */
+               if ((udf_rw16(dscr->pd.flags) & UDF_PART_FLAG_ALLOCATED) == 0) {
+                       free(dscr, M_UDFVOLD);
+                       break;
+               }
+
+               /*
+                * BUGALERT: some rogue implementations use random physical
+                * partition numbers to break other implementations so lookup
+                * the number.
+                */
+               raw_phys_part = udf_rw16(dscr->pd.part_num);
+               phys_part = udf_find_raw_phys(ump, raw_phys_part);
+
+               if (phys_part == UDF_PARTITIONS) {
+                       free(dscr, M_UDFVOLD);
+                       return EINVAL;
+               }
+
+               UDF_UPDATE_DSCR(ump->partitions[phys_part], &dscr->pd);
+               break;
+       case TAGID_VOL :                /* volume space extender; rare  */
+               DPRINTF(VOLUMES, ("VDS extender ignored\n"));
+               free(dscr, M_UDFVOLD);
+               break;
+       default :
+               DPRINTF(VOLUMES, ("Unhandled VDS type %d\n",
+                   udf_rw16(dscr->tag.id)));
+               free(dscr, M_UDFVOLD);
+       }
+
+       return 0;
+}
+#undef UDF_UPDATE_DSCR
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_read_vds_extent(struct udf_mount *ump, uint32_t loc, uint32_t len)
+{
+       union dscrptr *dscr;
+       uint32_t sector_size, dscr_size;
+       int error;
+
+       sector_size = ump->discinfo.sector_size;
+
+       /* loc is sectornr, len is in bytes */
+       error = EIO;
+       while (len) {
+               error = udf_read_phys_dscr(ump, loc, M_UDFVOLD, &dscr);
+               if (error)
+                       return error;
+
+               /* blank block is a terminator */
+               if (dscr == NULL)
+                       return 0;
+
+               /* TERM descriptor is a terminator */
+               if (udf_rw16(dscr->tag.id) == TAGID_TERM) {
+                       free(dscr, M_UDFVOLD);
+                       return 0;
+               }
+
+               /* process all others */
+               dscr_size = udf_tagsize(dscr, sector_size);
+               error = udf_process_vds_descriptor(ump, dscr);
+               if (error) {
+                       free(dscr, M_UDFVOLD);
+                       break;
+               }
+               assert((dscr_size % sector_size) == 0);
+
+               len -= dscr_size;
+               loc += dscr_size / sector_size;
+       }
+
+       return error;
+}
+
+
+int
+udf_read_vds_space(struct udf_mount *ump)
+{
+       /* struct udf_args *args = &ump->mount_args; */
+       struct anchor_vdp *anchor, *anchor2;
+       size_t size;
+       uint32_t main_loc, main_len;
+       uint32_t reserve_loc, reserve_len;
+       int error;
+
+       /*
+        * read in VDS space provided by the anchors; if one descriptor read
+        * fails, try the mirror sector.
+        *
+        * check if 2nd anchor is different from 1st; if so, go for 2nd. This
+        * avoids the `compatibility features' of DirectCD that may confuse
+        * stuff completely.
+        */
+
+       anchor  = ump->anchors[0];
+       anchor2 = ump->anchors[1];
+       assert(anchor);
+
+       if (anchor2) {
+               size = sizeof(struct extent_ad);
+               if (memcmp(&anchor->main_vds_ex, &anchor2->main_vds_ex, size))
+                       anchor = anchor2;
+               /* reserve is specified to be a literal copy of main */
+       }
+
+       main_loc    = udf_rw32(anchor->main_vds_ex.loc);
+       main_len    = udf_rw32(anchor->main_vds_ex.len);
+
+       reserve_loc = udf_rw32(anchor->reserve_vds_ex.loc);
+       reserve_len = udf_rw32(anchor->reserve_vds_ex.len);
+
+       error = udf_read_vds_extent(ump, main_loc, main_len);
+       if (error) {
+               printf("UDF mount: reading in reserve VDS extent\n");
+               error = udf_read_vds_extent(ump, reserve_loc, reserve_len);
+       }
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Read in the logical volume integrity sequence pointed to by our logical
+ * volume descriptor. Its a sequence that can be extended using fields in the
+ * integrity descriptor itself. On sequential media only one is found, on
+ * rewritable media a sequence of descriptors can be found as a form of
+ * history keeping and on non sequential write-once media the chain is vital
+ * to allow more and more descriptors to be written. The last descriptor
+ * written in an extent needs to claim space for a new extent.
+ */
+
+static int
+udf_retrieve_lvint(struct udf_mount *ump)
+{
+       union dscrptr *dscr;
+       struct logvol_int_desc *lvint;
+       struct udf_lvintq *trace;
+       uint32_t lb_size, lbnum, len;
+       int dscr_type, error, trace_len;
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       len     = udf_rw32(ump->logical_vol->integrity_seq_loc.len);
+       lbnum   = udf_rw32(ump->logical_vol->integrity_seq_loc.loc);
+
+       /* clean trace */
+       memset(ump->lvint_trace, 0,
+           UDF_LVDINT_SEGMENTS * sizeof(struct udf_lvintq));
+
+       trace_len    = 0;
+       trace        = ump->lvint_trace;
+       trace->start = lbnum;
+       trace->end   = lbnum + len/lb_size;
+       trace->pos   = 0;
+       trace->wpos  = 0;
+
+       lvint = NULL;
+       dscr  = NULL;
+       error = 0;
+       while (len) {
+               trace->pos  = lbnum - trace->start;
+               trace->wpos = trace->pos + 1;
+
+               /* read in our integrity descriptor */
+               error = udf_read_phys_dscr(ump, lbnum, M_UDFVOLD, &dscr);
+               if (!error) {
+                       if (dscr == NULL) {
+                               trace->wpos = trace->pos;
+                               break;          /* empty terminates */
+                       }
+                       dscr_type = udf_rw16(dscr->tag.id);
+                       if (dscr_type == TAGID_TERM) {
+                               trace->wpos = trace->pos;
+                               break;          /* clean terminator */
+                       }
+                       if (dscr_type != TAGID_LOGVOL_INTEGRITY) {
+                               /* fatal... corrupt disc */
+                               error = ENOENT;
+                               break;
+                       }
+                       if (lvint)
+                               free(lvint, M_UDFVOLD);
+                       lvint = &dscr->lvid;
+                       dscr = NULL;
+               } /* else hope for the best... maybe the next is ok */
+
+               DPRINTFIF(VOLUMES, lvint, ("logvol integrity read, state %s\n",
+                   udf_rw32(lvint->integrity_type) ? "CLOSED" : "OPEN"));
+
+               /* proceed sequential */
+               lbnum += 1;
+               len    -= lb_size;
+
+               /* are we linking to a new piece? */
+               if (dscr && lvint->next_extent.len) {
+                       len    = udf_rw32(lvint->next_extent.len);
+                       lbnum = udf_rw32(lvint->next_extent.loc);
+
+                       if (trace_len >= UDF_LVDINT_SEGMENTS-1) {
+                               /* IEK! segment link full... */
+                               DPRINTF(VOLUMES, ("lvdint segments full\n"));
+                               error = EINVAL;
+                       } else {
+                               trace++;
+                               trace_len++;
+
+                               trace->start = lbnum;
+                               trace->end   = lbnum + len/lb_size;
+                               trace->pos   = 0;
+                               trace->wpos  = 0;
+                       }
+               }
+       }
+
+       /* clean up the mess, esp. when there is an error */
+       if (dscr)
+               free(dscr, M_UDFVOLD);
+
+       if (error && lvint) {
+               free(lvint, M_UDFVOLD);
+               lvint = NULL;
+       }
+
+       if (!lvint)
+               error = ENOENT;
+
+       ump->logvol_integrity = lvint;
+       return error;
+}
+
+
+static int
+udf_loose_lvint_history(struct udf_mount *ump)
+{
+       union dscrptr **bufs, *dscr, *last_dscr;
+       struct udf_lvintq *trace, *in_trace, *out_trace;
+       struct logvol_int_desc *lvint;
+       uint32_t in_ext, in_pos, in_len;
+       uint32_t out_ext, out_wpos, out_len;
+       uint32_t lb_num;
+       uint32_t len, start;
+       int ext, minext, extlen, cnt, cpy_len, dscr_type;
+       int losing;
+       int error;
+
+       DPRINTF(VOLUMES, ("need to lose some lvint history\n"));
+
+       /* search smallest extent */
+       trace = &ump->lvint_trace[0];
+       minext = trace->end - trace->start;
+       for (ext = 1; ext < UDF_LVDINT_SEGMENTS; ext++) {
+               trace = &ump->lvint_trace[ext];
+               extlen = trace->end - trace->start;
+               if (extlen == 0)
+                       break;
+               minext = MIN(minext, extlen);
+       }
+       losing = MIN(minext, UDF_LVINT_LOSSAGE);
+       /* no sense wiping all */
+       if (losing == minext)
+               losing--;
+
+       DPRINTF(VOLUMES, ("\tlosing %d entries\n", losing));
+
+       /* get buffer for pieces */
+       bufs = malloc(UDF_LVDINT_SEGMENTS * sizeof(void *), M_TEMP, M_WAITOK);
+
+       in_ext    = 0;
+       in_pos    = losing;
+       in_trace  = &ump->lvint_trace[in_ext];
+       in_len    = in_trace->end - in_trace->start;
+       out_ext   = 0;
+       out_wpos  = 0;
+       out_trace = &ump->lvint_trace[out_ext];
+       out_len   = out_trace->end - out_trace->start;
+
+       last_dscr = NULL;
+       for(;;) {
+               out_trace->pos  = out_wpos;
+               out_trace->wpos = out_trace->pos;
+               if (in_pos >= in_len) {
+                       in_ext++;
+                       in_pos = 0;
+                       in_trace = &ump->lvint_trace[in_ext];
+                       in_len   = in_trace->end - in_trace->start;
+               }
+               if (out_wpos >= out_len) {
+                       out_ext++;
+                       out_wpos = 0;
+                       out_trace = &ump->lvint_trace[out_ext];
+                       out_len   = out_trace->end - out_trace->start;
+               }
+               /* copy overlap contents */
+               cpy_len = MIN(in_len - in_pos, out_len - out_wpos);
+               cpy_len = MIN(cpy_len, in_len - in_trace->pos);
+               if (cpy_len == 0)
+                       break;
+
+               /* copy */
+               DPRINTF(VOLUMES, ("\treading %d lvid descriptors\n", cpy_len));
+               for (cnt = 0; cnt < cpy_len; cnt++) {
+                       /* read in our integrity descriptor */
+                       lb_num = in_trace->start + in_pos + cnt;
+                       error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD,
+                               &dscr);
+                       if (error) {
+                               /* copy last one */
+                               dscr = last_dscr;
+                       }
+                       bufs[cnt] = dscr;
+                       if (!error) {
+                               if (dscr == NULL) {
+                                       out_trace->pos  = out_wpos + cnt;
+                                       out_trace->wpos = out_trace->pos;
+                                       break;          /* empty terminates */
+                               }
+                               dscr_type = udf_rw16(dscr->tag.id);
+                               if (dscr_type == TAGID_TERM) {
+                                       out_trace->pos  = out_wpos + cnt;
+                                       out_trace->wpos = out_trace->pos;
+                                       break;          /* clean terminator */
+                               }
+                               if (dscr_type != TAGID_LOGVOL_INTEGRITY) {
+                                       panic(  "UDF integrity sequence "
+                                               "corrupted while mounted!\n");
+                               }
+                               last_dscr = dscr;
+                       }
+               }
+
+               /* patch up if first entry was on error */
+               if (bufs[0] == NULL) {
+                       for (cnt = 0; cnt < cpy_len; cnt++)
+                               if (bufs[cnt] != NULL)
+                                       break;
+                       last_dscr = bufs[cnt];
+                       for (; cnt > 0; cnt--) {
+                               bufs[cnt] = last_dscr;
+                       }
+               }
+
+               /* glue + write out */
+               DPRINTF(VOLUMES, ("\twriting %d lvid descriptors\n", cpy_len));
+               for (cnt = 0; cnt < cpy_len; cnt++) {
+                       lb_num = out_trace->start + out_wpos + cnt;
+                       lvint  = &bufs[cnt]->lvid;
+
+                       /* set continuation */
+                       len = 0;
+                       start = 0;
+                       if (out_wpos + cnt == out_len) {
+                               /* get continuation */
+                               trace = &ump->lvint_trace[out_ext+1];
+                               len   = trace->end - trace->start;
+                               start = trace->start;
+                       }
+                       lvint->next_extent.len = udf_rw32(len);
+                       lvint->next_extent.loc = udf_rw32(start);
+
+                       lb_num = trace->start + trace->wpos;
+                       error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR,
+                               bufs[cnt], lb_num, lb_num);
+                       DPRINTFIF(VOLUMES, error,
+                               ("error writing lvint lb_num\n"));
+               }
+
+               /* free non repeating descriptors */
+               last_dscr = NULL;
+               for (cnt = 0; cnt < cpy_len; cnt++) {
+                       if (bufs[cnt] != last_dscr)
+                               free(bufs[cnt], M_UDFVOLD);
+                       last_dscr = bufs[cnt];
+               }
+
+               /* advance */
+               in_pos   += cpy_len;
+               out_wpos += cpy_len;
+       }
+
+       free(bufs, M_TEMP);
+
+       return 0;
+}
+
+
+static int
+udf_writeout_lvint(struct udf_mount *ump, int lvflag)
+{
+       struct udf_lvintq *trace;
+       struct timeval  now_v;
+       struct timespec now_s;
+       uint32_t sector;
+       int logvol_integrity;
+       int space, error;
+
+       DPRINTF(VOLUMES, ("writing out logvol integrity descriptor\n"));
+
+again:
+       /* get free space in last chunk */
+       trace = ump->lvint_trace;
+       while (trace->wpos > (trace->end - trace->start)) {
+               DPRINTF(VOLUMES, ("skip : start = %d, end = %d, pos = %d, "
+                                 "wpos = %d\n", trace->start, trace->end,
+                                 trace->pos, trace->wpos));
+               trace++;
+       }
+
+       /* check if there is space to append */
+       space = (trace->end - trace->start) - trace->wpos;
+       DPRINTF(VOLUMES, ("write start = %d, end = %d, pos = %d, wpos = %d, "
+                         "space = %d\n", trace->start, trace->end, trace->pos,
+                         trace->wpos, space));
+
+       /* get state */
+       logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type);
+       if (logvol_integrity == UDF_INTEGRITY_CLOSED) {
+               if ((space < 3) && (lvflag & UDF_APPENDONLY_LVINT)) {
+                       /* TODO extent LVINT space if possible */
+                       return EROFS;
+               }
+       }
+
+       if (space < 1) {
+               if (lvflag & UDF_APPENDONLY_LVINT)
+                       return EROFS;
+               /* loose history by re-writing extents */
+               error = udf_loose_lvint_history(ump);
+               if (error)
+                       return error;
+               goto again;
+       }
+
+       /* update our integrity descriptor to identify us and timestamp it */
+       DPRINTF(VOLUMES, ("updating integrity descriptor\n"));
+       microtime(&now_v);
+       TIMEVAL_TO_TIMESPEC(&now_v, &now_s);
+       udf_timespec_to_timestamp(&now_s, &ump->logvol_integrity->time);
+       udf_set_regid(&ump->logvol_info->impl_id, IMPL_NAME);
+       udf_add_impl_regid(ump, &ump->logvol_info->impl_id);
+
+       /* writeout integrity descriptor */
+       sector = trace->start + trace->wpos;
+       error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR,
+                       (union dscrptr *) ump->logvol_integrity,
+                       sector, sector);
+       DPRINTF(VOLUMES, ("writeout lvint : error = %d\n", error));
+       if (error)
+               return error;
+
+       /* advance write position */
+       trace->wpos++; space--;
+       if (space >= 1) {
+               /* append terminator */
+               sector = trace->start + trace->wpos;
+               error = udf_write_terminator(ump, sector);
+
+               DPRINTF(VOLUMES, ("write terminator : error = %d\n", error));
+       }
+
+       space = (trace->end - trace->start) - trace->wpos;
+       DPRINTF(VOLUMES, ("write start = %d, end = %d, pos = %d, wpos = %d, "
+               "space = %d\n", trace->start, trace->end, trace->pos,
+               trace->wpos, space));
+       DPRINTF(VOLUMES, ("finished writing out logvol integrity descriptor "
+               "successfull\n"));
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_read_physical_partition_spacetables(struct udf_mount *ump)
+{
+       union dscrptr        *dscr;
+       /* struct udf_args *args = &ump->mount_args; */
+       struct part_desc     *partd;
+       struct part_hdr_desc *parthdr;
+       struct udf_bitmap    *bitmap;
+       uint32_t phys_part;
+       uint32_t lb_num, len;
+       int error, dscr_type;
+
+       /* unallocated space map */
+       for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) {
+               partd = ump->partitions[phys_part];
+               if (partd == NULL)
+                       continue;
+               parthdr = &partd->_impl_use.part_hdr;
+
+               lb_num  = udf_rw32(partd->start_loc);
+               lb_num += udf_rw32(parthdr->unalloc_space_bitmap.lb_num);
+               len     = udf_rw32(parthdr->unalloc_space_bitmap.len);
+               if (len == 0)
+                       continue;
+
+               DPRINTF(VOLUMES, ("Read unalloc. space bitmap %d\n", lb_num));
+               error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr);
+               if (!error && dscr) {
+                       /* analyse */
+                       dscr_type = udf_rw16(dscr->tag.id);
+                       if (dscr_type == TAGID_SPACE_BITMAP) {
+                               DPRINTF(VOLUMES, ("Accepting space bitmap\n"));
+                               ump->part_unalloc_dscr[phys_part] = &dscr->sbd;
+
+                               /* fill in ump->part_unalloc_bits */
+                               bitmap = &ump->part_unalloc_bits[phys_part];
+                               bitmap->blob  = (uint8_t *) dscr;
+                               bitmap->bits  = dscr->sbd.data;
+                               bitmap->max_offset = udf_rw32(dscr->sbd.num_bits);
+                               bitmap->pages = NULL;   /* TODO */
+                               bitmap->data_pos     = 0;
+                               bitmap->metadata_pos = 0;
+                       } else {
+                               free(dscr, M_UDFVOLD);
+
+                               printf( "UDF mount: error reading unallocated "
+                                       "space bitmap\n");
+                               return EROFS;
+                       }
+               } else {
+                       /* blank not allowed */
+                       printf("UDF mount: blank unallocated space bitmap\n");
+                       return EROFS;
+               }
+       }
+
+       /* unallocated space table (not supported) */
+       for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) {
+               partd = ump->partitions[phys_part];
+               if (partd == NULL)
+                       continue;
+               parthdr = &partd->_impl_use.part_hdr;
+       
+               len     = udf_rw32(parthdr->unalloc_space_table.len);
+               if (len) {
+                       printf("UDF mount: space tables not supported\n");
+                       return EROFS;
+               }
+       }
+
+       /* freed space map */
+       for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) {
+               partd = ump->partitions[phys_part];
+               if (partd == NULL)
+                       continue;
+               parthdr = &partd->_impl_use.part_hdr;
+
+               /* freed space map */
+               lb_num  = udf_rw32(partd->start_loc);
+               lb_num += udf_rw32(parthdr->freed_space_bitmap.lb_num);
+               len     = udf_rw32(parthdr->freed_space_bitmap.len);
+               if (len == 0)
+                       continue;
+
+               DPRINTF(VOLUMES, ("Read unalloc. space bitmap %d\n", lb_num));
+               error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr);
+               if (!error && dscr) {
+                       /* analyse */
+                       dscr_type = udf_rw16(dscr->tag.id);
+                       if (dscr_type == TAGID_SPACE_BITMAP) {
+                               DPRINTF(VOLUMES, ("Accepting space bitmap\n"));
+                               ump->part_freed_dscr[phys_part] = &dscr->sbd;
+
+                               /* fill in ump->part_freed_bits */
+                               bitmap = &ump->part_unalloc_bits[phys_part];
+                               bitmap->blob  = (uint8_t *) dscr;
+                               bitmap->bits  = dscr->sbd.data;
+                               bitmap->max_offset = udf_rw32(dscr->sbd.num_bits);
+                               bitmap->pages = NULL;   /* TODO */
+                               bitmap->data_pos     = 0;
+                               bitmap->metadata_pos = 0;
+                       } else {
+                               free(dscr, M_UDFVOLD);
+
+                               printf( "UDF mount: error reading freed  "
+                                       "space bitmap\n");
+                               return EROFS;
+                       }
+               } else {
+                       /* blank not allowed */
+                       printf("UDF mount: blank freed space bitmap\n");
+                       return EROFS;
+               }
+       }
+
+       /* freed space table (not supported) */
+       for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) {
+               partd = ump->partitions[phys_part];
+               if (partd == NULL)
+                       continue;
+               parthdr = &partd->_impl_use.part_hdr;
+       
+               len     = udf_rw32(parthdr->freed_space_table.len);
+               if (len) {
+                       printf("UDF mount: space tables not supported\n");
+                       return EROFS;
+               }
+       }
+
+       return 0;
+}
+
+
+/* TODO implement async writeout */
+int
+udf_write_physical_partition_spacetables(struct udf_mount *ump, int waitfor)
+{
+       union dscrptr        *dscr;
+       /* struct udf_args *args = &ump->mount_args; */
+       struct part_desc     *partd;
+       struct part_hdr_desc *parthdr;
+       uint32_t phys_part;
+       uint32_t lb_num, len, ptov;
+       int error_all, error;
+
+       error_all = 0;
+       /* unallocated space map */
+       for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) {
+               partd = ump->partitions[phys_part];
+               if (partd == NULL)
+                       continue;
+               parthdr = &partd->_impl_use.part_hdr;
+
+               ptov   = udf_rw32(partd->start_loc);
+               lb_num = udf_rw32(parthdr->unalloc_space_bitmap.lb_num);
+               len    = udf_rw32(parthdr->unalloc_space_bitmap.len);
+               if (len == 0)
+                       continue;
+
+               DPRINTF(VOLUMES, ("Write unalloc. space bitmap %d\n",
+                       lb_num + ptov));
+               dscr = (union dscrptr *) ump->part_unalloc_dscr[phys_part];
+               error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR,
+                               (union dscrptr *) dscr,
+                               ptov + lb_num, lb_num);
+               if (error) {
+                       DPRINTF(VOLUMES, ("\tfailed!! (error %d)\n", error));
+                       error_all = error;
+               }
+       }
+
+       /* freed space map */
+       for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) {
+               partd = ump->partitions[phys_part];
+               if (partd == NULL)
+                       continue;
+               parthdr = &partd->_impl_use.part_hdr;
+
+               /* freed space map */
+               ptov   = udf_rw32(partd->start_loc);
+               lb_num = udf_rw32(parthdr->freed_space_bitmap.lb_num);
+               len    = udf_rw32(parthdr->freed_space_bitmap.len);
+               if (len == 0)
+                       continue;
+
+               DPRINTF(VOLUMES, ("Write freed space bitmap %d\n",
+                       lb_num + ptov));
+               dscr = (union dscrptr *) ump->part_freed_dscr[phys_part];
+               error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR,
+                               (union dscrptr *) dscr,
+                               ptov + lb_num, lb_num);
+               if (error) {
+                       DPRINTF(VOLUMES, ("\tfailed!! (error %d)\n", error));
+                       error_all = error;
+               }
+       }
+
+       return error_all;
+}
+
+
+static int
+udf_read_metadata_partition_spacetable(struct udf_mount *ump)
+{
+       struct udf_node      *bitmap_node;
+       union dscrptr        *dscr;
+       struct udf_bitmap    *bitmap;
+       uint64_t inflen;
+       int error, dscr_type;
+
+       bitmap_node = ump->metadatabitmap_node;
+
+       /* only read in when metadata bitmap node is read in */
+       if (bitmap_node == NULL)
+               return 0;
+
+       if (bitmap_node->fe) {
+               inflen = udf_rw64(bitmap_node->fe->inf_len);
+       } else {
+               KASSERT(bitmap_node->efe);
+               inflen = udf_rw64(bitmap_node->efe->inf_len);
+       }
+
+       DPRINTF(VOLUMES, ("Reading metadata space bitmap for "
+               "%"PRIu64" bytes\n", inflen));
+
+       /* allocate space for bitmap */
+       dscr = malloc(inflen, M_UDFVOLD, M_CANFAIL | M_WAITOK);
+       if (!dscr)
+               return ENOMEM;
+
+       /* set vnode type to regular file or we can't read from it! */
+       bitmap_node->vnode->v_type = VREG;
+
+       /* read in complete metadata bitmap file */
+       error = vn_rdwr(UIO_READ, bitmap_node->vnode,
+                       dscr,
+                       inflen, 0,
+                       UIO_SYSSPACE,
+                       IO_SYNC | IO_ALTSEMANTICS, FSCRED,
+                       NULL, NULL);
+       if (error) {
+               DPRINTF(VOLUMES, ("Error reading metadata space bitmap\n"));
+               goto errorout;
+       }
+
+       /* analyse */
+       dscr_type = udf_rw16(dscr->tag.id);
+       if (dscr_type == TAGID_SPACE_BITMAP) {
+               DPRINTF(VOLUMES, ("Accepting metadata space bitmap\n"));
+               ump->metadata_unalloc_dscr = &dscr->sbd;
+
+               /* fill in bitmap bits */
+               bitmap = &ump->metadata_unalloc_bits;
+               bitmap->blob  = (uint8_t *) dscr;
+               bitmap->bits  = dscr->sbd.data;
+               bitmap->max_offset = udf_rw32(dscr->sbd.num_bits);
+               bitmap->pages = NULL;   /* TODO */
+               bitmap->data_pos     = 0;
+               bitmap->metadata_pos = 0;
+       } else {
+               DPRINTF(VOLUMES, ("No valid bitmap found!\n"));
+               goto errorout;
+       }
+
+       return 0;
+
+errorout:
+       free(dscr, M_UDFVOLD);
+       printf( "UDF mount: error reading unallocated "
+               "space bitmap for metadata partition\n");
+       return EROFS;
+}
+
+
+int
+udf_write_metadata_partition_spacetable(struct udf_mount *ump, int waitfor)
+{
+       struct udf_node      *bitmap_node;
+       union dscrptr        *dscr;
+       uint64_t new_inflen;
+       int dummy, error;
+
+       bitmap_node = ump->metadatabitmap_node;
+
+       /* only write out when metadata bitmap node is known */
+       if (bitmap_node == NULL)
+               return 0;
+
+       if (!bitmap_node->fe) {
+               KASSERT(bitmap_node->efe);
+       }
+
+       /* reduce length to zero */
+       dscr = (union dscrptr *) ump->metadata_unalloc_dscr;
+       new_inflen = udf_tagsize(dscr, 1);
+
+       DPRINTF(VOLUMES, ("Resize and write out metadata space bitmap from "
+               "%"PRIu64" to %"PRIu64" bytes\n", inflen, new_inflen));
+
+       error = udf_resize_node(bitmap_node, new_inflen, &dummy);
+       if (error)
+               printf("Error resizing metadata space bitmap\n");
+
+       error = vn_rdwr(UIO_WRITE, bitmap_node->vnode,
+                       dscr,
+                       new_inflen, 0,
+                       UIO_SYSSPACE,
+                       IO_ALTSEMANTICS, FSCRED,
+                       NULL, NULL);
+
+       bitmap_node->i_flags |= IN_MODIFIED;
+       error = vflushbuf(bitmap_node->vnode, FSYNC_WAIT);
+       if (error == 0)
+               error = VOP_FSYNC(bitmap_node->vnode,
+                               FSCRED, FSYNC_WAIT, 0, 0);
+
+       if (error)
+               printf( "Error writing out metadata partition unalloced "
+                       "space bitmap!\n");
+
+       return error;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Checks if ump's vds information is correct and complete
+ */
+
+int
+udf_process_vds(struct udf_mount *ump) {
+       union udf_pmap *mapping;
+       /* struct udf_args *args = &ump->mount_args; */
+       struct logvol_int_desc *lvint;
+       struct udf_logvol_info *lvinfo;
+       uint32_t n_pm;
+       uint8_t *pmap_pos;
+       char *domain_name, *map_name;
+       const char *check_name;
+       char bits[128];
+       int pmap_stype, pmap_size;
+       int pmap_type, log_part, phys_part, raw_phys_part, maps_on;
+       int n_phys, n_virt, n_spar, n_meta;
+       int len;
+
+       if (ump == NULL)
+               return ENOENT;
+
+       /* we need at least an anchor (trivial, but for safety) */
+       if (ump->anchors[0] == NULL)
+               return EINVAL;
+
+       /* we need at least one primary and one logical volume descriptor */
+       if ((ump->primary_vol == NULL) || (ump->logical_vol) == NULL)
+               return EINVAL;
+
+       /* we need at least one partition descriptor */
+       if (ump->partitions[0] == NULL)
+               return EINVAL;
+
+       /* check logical volume sector size verses device sector size */
+       if (udf_rw32(ump->logical_vol->lb_size) != ump->discinfo.sector_size) {
+               printf("UDF mount: format violation, lb_size != sector size\n");
+               return EINVAL;
+       }
+
+       /* check domain name */
+       domain_name = ump->logical_vol->domain_id.id;
+       if (strncmp(domain_name, "*OSTA UDF Compliant", 20)) {
+               printf("mount_udf: disc not OSTA UDF Compliant, aborting\n");
+               return EINVAL;
+       }
+
+       /* retrieve logical volume integrity sequence */
+       (void)udf_retrieve_lvint(ump);
+
+       /*
+        * We need at least one logvol integrity descriptor recorded.  Note
+        * that its OK to have an open logical volume integrity here. The VAT
+        * will close/update the integrity.
+        */
+       if (ump->logvol_integrity == NULL)
+               return EINVAL;
+
+       /* process derived structures */
+       n_pm   = udf_rw32(ump->logical_vol->n_pm);   /* num partmaps         */
+       lvint  = ump->logvol_integrity;
+       lvinfo = (struct udf_logvol_info *) (&lvint->tables[2 * n_pm]);
+       ump->logvol_info = lvinfo;
+
+       /* TODO check udf versions? */
+
+       /*
+        * check logvol mappings: effective virt->log partmap translation
+        * check and recording of the mapping results. Saves expensive
+        * strncmp() in tight places.
+        */
+       DPRINTF(VOLUMES, ("checking logvol mappings\n"));
+       n_pm = udf_rw32(ump->logical_vol->n_pm);   /* num partmaps         */
+       pmap_pos =  ump->logical_vol->maps;
+
+       if (n_pm > UDF_PMAPS) {
+               printf("UDF mount: too many mappings\n");
+               return EINVAL;
+       }
+
+       /* count types and set partition numbers */
+       ump->data_part = ump->node_part = ump->fids_part = 0;
+       n_phys = n_virt = n_spar = n_meta = 0;
+       for (log_part = 0; log_part < n_pm; log_part++) {
+               mapping = (union udf_pmap *) pmap_pos;
+               pmap_stype = pmap_pos[0];
+               pmap_size  = pmap_pos[1];
+               switch (pmap_stype) {
+               case 1: /* physical mapping */
+                       /* volseq    = udf_rw16(mapping->pm1.vol_seq_num); */
+                       raw_phys_part = udf_rw16(mapping->pm1.part_num);
+                       pmap_type = UDF_VTOP_TYPE_PHYS;
+                       n_phys++;
+                       ump->data_part = log_part;
+                       ump->node_part = log_part;
+                       ump->fids_part = log_part;
+                       break;
+               case 2: /* virtual/sparable/meta mapping */
+                       map_name  = mapping->pm2.part_id.id;
+                       /* volseq  = udf_rw16(mapping->pm2.vol_seq_num); */
+                       raw_phys_part = udf_rw16(mapping->pm2.part_num);
+                       pmap_type = UDF_VTOP_TYPE_UNKNOWN;
+                       len = UDF_REGID_ID_SIZE;
+
+                       check_name = "*UDF Virtual Partition";
+                       if (strncmp(map_name, check_name, len) == 0) {
+                               pmap_type = UDF_VTOP_TYPE_VIRT;
+                               n_virt++;
+                               ump->node_part = log_part;
+                               break;
+                       }
+                       check_name = "*UDF Sparable Partition";
+                       if (strncmp(map_name, check_name, len) == 0) {
+                               pmap_type = UDF_VTOP_TYPE_SPARABLE;
+                               n_spar++;
+                               ump->data_part = log_part;
+                               ump->node_part = log_part;
+                               ump->fids_part = log_part;
+                               break;
+                       }
+                       check_name = "*UDF Metadata Partition";
+                       if (strncmp(map_name, check_name, len) == 0) {
+                               pmap_type = UDF_VTOP_TYPE_META;
+                               n_meta++;
+                               ump->node_part = log_part;
+                               ump->fids_part = log_part;
+                               break;
+                       }
+                       break;
+               default:
+                       return EINVAL;
+               }
+
+               /*
+                * BUGALERT: some rogue implementations use random physical
+                * partition numbers to break other implementations so lookup
+                * the number.
+                */
+               phys_part = udf_find_raw_phys(ump, raw_phys_part);
+
+               DPRINTF(VOLUMES, ("\t%d -> %d(%d) type %d\n", log_part,
+                   raw_phys_part, phys_part, pmap_type));
+       
+               if (phys_part == UDF_PARTITIONS)
+                       return EINVAL;
+               if (pmap_type == UDF_VTOP_TYPE_UNKNOWN)
+                       return EINVAL;
+
+               ump->vtop   [log_part] = phys_part;
+               ump->vtop_tp[log_part] = pmap_type;
+
+               pmap_pos += pmap_size;
+       }
+       /* not winning the beauty contest */
+       ump->vtop_tp[UDF_VTOP_RAWPART] = UDF_VTOP_TYPE_RAW;
+
+       /* test some basic UDF assertions/requirements */
+       if ((n_virt > 1) || (n_spar > 1) || (n_meta > 1))
+               return EINVAL;
+
+       if (n_virt) {
+               if ((n_phys == 0) || n_spar || n_meta)
+                       return EINVAL;
+       }
+       if (n_spar + n_phys == 0)
+               return EINVAL;
+
+       /* select allocation type for each logical partition */
+       for (log_part = 0; log_part < n_pm; log_part++) {
+               maps_on = ump->vtop[log_part];
+               switch (ump->vtop_tp[log_part]) {
+               case UDF_VTOP_TYPE_PHYS :
+                       assert(maps_on == log_part);
+                       ump->vtop_alloc[log_part] = UDF_ALLOC_SPACEMAP;
+                       break;
+               case UDF_VTOP_TYPE_VIRT :
+                       ump->vtop_alloc[log_part] = UDF_ALLOC_VAT;
+                       ump->vtop_alloc[maps_on]  = UDF_ALLOC_SEQUENTIAL;
+                       break;
+               case UDF_VTOP_TYPE_SPARABLE :
+                       assert(maps_on == log_part);
+                       ump->vtop_alloc[log_part] = UDF_ALLOC_SPACEMAP;
+                       break;
+               case UDF_VTOP_TYPE_META :
+                       ump->vtop_alloc[log_part] = UDF_ALLOC_METABITMAP;
+                       if (ump->discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE) {
+                               /* special case for UDF 2.60 */
+                               ump->vtop_alloc[log_part] = UDF_ALLOC_METASEQUENTIAL;
+                               ump->vtop_alloc[maps_on]  = UDF_ALLOC_SEQUENTIAL;
+                       }
+                       break;
+               default:
+                       panic("bad alloction type in udf's ump->vtop\n");
+               }
+       }
+
+       /* determine logical volume open/closure actions */
+       if (n_virt) {
+               ump->lvopen  = 0;
+               if (ump->discinfo.last_session_state == MMC_STATE_EMPTY)
+                       ump->lvopen |= UDF_OPEN_SESSION ;
+               ump->lvclose = UDF_WRITE_VAT;
+               if (ump->mount_args.udfmflags & UDFMNT_CLOSESESSION)
+                       ump->lvclose |= UDF_CLOSE_SESSION;
+       } else {
+               /* `normal' rewritable or non sequential media */
+               ump->lvopen  = UDF_WRITE_LVINT;
+               ump->lvclose = UDF_WRITE_LVINT;
+               if ((ump->discinfo.mmc_cur & MMC_CAP_REWRITABLE) == 0)
+                       ump->lvopen  |=  UDF_APPENDONLY_LVINT;
+               if ((ump->discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE))
+                       ump->lvopen  &= ~UDF_APPENDONLY_LVINT;
+       }
+
+       /*
+        * Determine sheduler error behaviour. For virtual partitions, update
+        * the trackinfo; for sparable partitions replace a whole block on the
+        * sparable table. Allways requeue.
+        */
+       ump->lvreadwrite = 0;
+       if (n_virt)
+               ump->lvreadwrite = UDF_UPDATE_TRACKINFO;
+       if (n_spar)
+               ump->lvreadwrite = UDF_REMAP_BLOCK;
+
+       /*
+        * Select our sheduler
+        */
+       ump->strategy = &udf_strat_rmw;
+       if (n_virt || (ump->discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE))
+               ump->strategy = &udf_strat_sequential;
+       if ((ump->discinfo.mmc_class == MMC_CLASS_DISC) ||
+               (ump->discinfo.mmc_class == MMC_CLASS_UNKN))
+                       ump->strategy = &udf_strat_direct;
+       if (n_spar)
+               ump->strategy = &udf_strat_rmw;
+
+#if 0
+       /* read-only access won't benefit from the other shedulers */
+       if (ump->vfs_mountp->mnt_flag & MNT_RDONLY)
+               ump->strategy = &udf_strat_direct;
+#endif
+
+       /* print results */
+       DPRINTF(VOLUMES, ("\tdata partition    %d\n", ump->data_part));
+       DPRINTF(VOLUMES, ("\t\talloc scheme %d\n", ump->vtop_alloc[ump->data_part]));
+       DPRINTF(VOLUMES, ("\tnode partition    %d\n", ump->node_part));
+       DPRINTF(VOLUMES, ("\t\talloc scheme %d\n", ump->vtop_alloc[ump->node_part]));
+       DPRINTF(VOLUMES, ("\tfids partition    %d\n", ump->fids_part));
+       DPRINTF(VOLUMES, ("\t\talloc scheme %d\n", ump->vtop_alloc[ump->fids_part]));
+
+       snprintb(bits, sizeof(bits), UDFLOGVOL_BITS, ump->lvopen);
+       DPRINTF(VOLUMES, ("\tactions on logvol open  %s\n", bits));
+       snprintb(bits, sizeof(bits), UDFLOGVOL_BITS, ump->lvclose);
+       DPRINTF(VOLUMES, ("\tactions on logvol close %s\n", bits));
+       snprintb(bits, sizeof(bits), UDFONERROR_BITS, ump->lvreadwrite);
+       DPRINTF(VOLUMES, ("\tactions on logvol errors %s\n", bits));
+
+       DPRINTF(VOLUMES, ("\tselected sheduler `%s`\n", 
+               (ump->strategy == &udf_strat_direct) ? "Direct" :
+               (ump->strategy == &udf_strat_sequential) ? "Sequential" :
+               (ump->strategy == &udf_strat_rmw) ? "RMW" : "UNKNOWN!"));
+
+       /* signal its OK for now */
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Update logical volume name in all structures that keep a record of it. We
+ * use memmove since each of them might be specified as a source.
+ *
+ * Note that it doesn't update the VAT structure!
+ */
+
+static void
+udf_update_logvolname(struct udf_mount *ump, char *logvol_id)
+{
+       struct logvol_desc     *lvd = NULL;
+       struct fileset_desc    *fsd = NULL;
+       struct udf_lv_info     *lvi = NULL;
+
+       DPRINTF(VOLUMES, ("Updating logical volume name\n"));
+       lvd = ump->logical_vol;
+       fsd = ump->fileset_desc;
+       if (ump->implementation)
+               lvi = &ump->implementation->_impl_use.lv_info;
+
+       /* logvol's id might be specified as origional so use memmove here */
+       memmove(lvd->logvol_id, logvol_id, 128);
+       if (fsd)
+               memmove(fsd->logvol_id, logvol_id, 128);
+       if (lvi)
+               memmove(lvi->logvol_id, logvol_id, 128);
+}
+
+/* --------------------------------------------------------------------- */
+
+void
+udf_inittag(struct udf_mount *ump, struct desc_tag *tag, int tagid,
+       uint32_t sector)
+{
+       assert(ump->logical_vol);
+
+       tag->id                 = udf_rw16(tagid);
+       tag->descriptor_ver     = ump->logical_vol->tag.descriptor_ver;
+       tag->cksum              = 0;
+       tag->reserved           = 0;
+       tag->serial_num         = ump->logical_vol->tag.serial_num;
+       tag->tag_loc            = udf_rw32(sector);
+}
+
+
+uint64_t
+udf_advance_uniqueid(struct udf_mount *ump)
+{
+       uint64_t unique_id;
+
+       mutex_enter(&ump->logvol_mutex);
+       unique_id = udf_rw64(ump->logvol_integrity->lvint_next_unique_id);
+       if (unique_id < 0x10)
+               unique_id = 0x10;
+       ump->logvol_integrity->lvint_next_unique_id = udf_rw64(unique_id + 1);
+       mutex_exit(&ump->logvol_mutex);
+
+       return unique_id;
+}
+
+
+static void
+udf_adjust_filecount(struct udf_node *udf_node, int sign)
+{
+       struct udf_mount *ump = udf_node->ump;
+       uint32_t num_dirs, num_files;
+       int udf_file_type;
+
+       /* get file type */
+       if (udf_node->fe) {
+               udf_file_type = udf_node->fe->icbtag.file_type;
+       } else {
+               udf_file_type = udf_node->efe->icbtag.file_type;
+       }
+
+       /* adjust file count */
+       mutex_enter(&ump->allocate_mutex);
+       if (udf_file_type == UDF_ICB_FILETYPE_DIRECTORY) {
+               num_dirs = udf_rw32(ump->logvol_info->num_directories);
+               ump->logvol_info->num_directories =
+                       udf_rw32((num_dirs + sign));
+       } else {
+               num_files = udf_rw32(ump->logvol_info->num_files);
+               ump->logvol_info->num_files =
+                       udf_rw32((num_files + sign));
+       }
+       mutex_exit(&ump->allocate_mutex);
+}
+
+
+void
+udf_osta_charset(struct charspec *charspec)
+{
+       memset(charspec, 0, sizeof(struct charspec));
+       charspec->type = 0;
+       strcpy((char *) charspec->inf, "OSTA Compressed Unicode");
+}
+
+
+/* first call udf_set_regid and then the suffix */
+void
+udf_set_regid(struct regid *regid, char const *name)
+{
+       memset(regid, 0, sizeof(struct regid));
+       regid->flags    = 0;            /* not dirty and not protected */
+       strcpy((char *) regid->id, name);
+}
+
+
+void
+udf_add_domain_regid(struct udf_mount *ump, struct regid *regid)
+{
+       uint16_t *ver;
+
+       ver  = (uint16_t *) regid->id_suffix;
+       *ver = ump->logvol_info->min_udf_readver;
+}
+
+
+void
+udf_add_udf_regid(struct udf_mount *ump, struct regid *regid)
+{
+       uint16_t *ver;
+
+       ver  = (uint16_t *) regid->id_suffix;
+       *ver = ump->logvol_info->min_udf_readver;
+
+       regid->id_suffix[2] = 4;        /* unix */
+       regid->id_suffix[3] = 8;        /* NetBSD */
+}
+
+
+void
+udf_add_impl_regid(struct udf_mount *ump, struct regid *regid)
+{
+       regid->id_suffix[0] = 4;        /* unix */
+       regid->id_suffix[1] = 8;        /* NetBSD */
+}
+
+
+void
+udf_add_app_regid(struct udf_mount *ump, struct regid *regid)
+{
+       regid->id_suffix[0] = APP_VERSION_MAIN;
+       regid->id_suffix[1] = APP_VERSION_SUB;
+}
+
+static int
+udf_create_parentfid(struct udf_mount *ump, struct fileid_desc *fid,
+       struct long_ad *parent, uint64_t unique_id)
+{
+       /* the size of an empty FID is 38 but needs to be a multiple of 4 */
+       int fidsize = 40;
+
+       udf_inittag(ump, &fid->tag, TAGID_FID, udf_rw32(parent->loc.lb_num));
+       fid->file_version_num = udf_rw16(1);    /* UDF 2.3.4.1 */
+       fid->file_char = UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR;
+       fid->icb = *parent;
+       fid->icb.longad_uniqueid = udf_rw32((uint32_t) unique_id);
+       fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH);
+       (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid);
+
+       return fidsize;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Extended attribute support. UDF knows of 3 places for extended attributes:
+ *
+ * (a) inside the file's (e)fe in the length of the extended attribute area
+ * before the allocation descriptors/filedata
+ *
+ * (b) in a file referenced by (e)fe->ext_attr_icb and 
+ *
+ * (c) in the e(fe)'s associated stream directory that can hold various
+ * sub-files. In the stream directory a few fixed named subfiles are reserved
+ * for NT/Unix ACL's and OS/2 attributes.
+ * 
+ * NOTE: Extended attributes are read randomly but allways written
+ * *atomicaly*. For ACL's this interface is propably different but not known
+ * to me yet.
+ *
+ * Order of extended attributes in a space :
+ *   ECMA 167 EAs
+ *   Non block aligned Implementation Use EAs
+ *   Block aligned Implementation Use EAs
+ *   Application Use EAs
+ */
+
+static int
+udf_impl_extattr_check(struct impl_extattr_entry *implext)
+{
+       uint16_t   *spos;
+
+       if (strncmp(implext->imp_id.id, "*UDF", 4) == 0) {
+               /* checksum valid? */
+               DPRINTF(EXTATTR, ("checking UDF impl. attr checksum\n"));
+               spos = (uint16_t *) implext->data;
+               if (udf_rw16(*spos) != udf_ea_cksum((uint8_t *) implext))
+                       return EINVAL;
+       }
+       return 0;
+}
+
+static void
+udf_calc_impl_extattr_checksum(struct impl_extattr_entry *implext)
+{
+       uint16_t   *spos;
+
+       if (strncmp(implext->imp_id.id, "*UDF", 4) == 0) {
+               /* set checksum */
+               spos = (uint16_t *) implext->data;
+               *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext));
+       }
+}
+
+
+int
+udf_extattr_search_intern(struct udf_node *node,
+       uint32_t sattr, char const *sattrname,
+       uint32_t *offsetp, uint32_t *lengthp)
+{
+       struct extattrhdr_desc    *eahdr;
+       struct extattr_entry      *attrhdr;
+       struct impl_extattr_entry *implext;
+       uint32_t    offset, a_l, sector_size;
+        int32_t    l_ea;
+       uint8_t    *pos;
+       int         error;
+
+       /* get mountpoint */
+       sector_size = node->ump->discinfo.sector_size;
+
+       /* get information from fe/efe */
+       if (node->fe) {
+               l_ea  = udf_rw32(node->fe->l_ea);
+               eahdr = (struct extattrhdr_desc *) node->fe->data;
+       } else {
+               assert(node->efe);
+               l_ea  = udf_rw32(node->efe->l_ea);
+               eahdr = (struct extattrhdr_desc *) node->efe->data;
+       }
+
+       /* something recorded here? */
+       if (l_ea == 0)
+               return ENOENT;
+
+       /* check extended attribute tag; what to do if it fails? */
+       error = udf_check_tag(eahdr);
+       if (error)
+               return EINVAL;
+       if (udf_rw16(eahdr->tag.id) != TAGID_EXTATTR_HDR)
+               return EINVAL;
+       error = udf_check_tag_payload(eahdr, sizeof(struct extattrhdr_desc));
+       if (error)
+               return EINVAL;
+
+       DPRINTF(EXTATTR, ("Found %d bytes of extended attributes\n", l_ea));
+
+       /* looking for Ecma-167 attributes? */
+       offset = sizeof(struct extattrhdr_desc);
+
+       /* looking for either implemenation use or application use */
+       if (sattr == 2048) {                            /* [4/48.10.8] */
+               offset = udf_rw32(eahdr->impl_attr_loc);
+               if (offset == UDF_IMPL_ATTR_LOC_NOT_PRESENT)
+                       return ENOENT;
+       }
+       if (sattr == 65536) {                           /* [4/48.10.9] */
+               offset = udf_rw32(eahdr->appl_attr_loc);
+               if (offset == UDF_APPL_ATTR_LOC_NOT_PRESENT)
+                       return ENOENT;
+       }
+
+       /* paranoia check offset and l_ea */
+       if (l_ea + offset >= sector_size - sizeof(struct extattr_entry))
+               return EINVAL;
+
+       DPRINTF(EXTATTR, ("Starting at offset %d\n", offset));
+
+       /* find our extended attribute  */
+       l_ea -= offset;
+       pos = (uint8_t *) eahdr + offset;
+
+       while (l_ea >= sizeof(struct extattr_entry)) {
+               DPRINTF(EXTATTR, ("%d extended attr bytes left\n", l_ea));
+               attrhdr = (struct extattr_entry *) pos;
+               implext = (struct impl_extattr_entry *) pos;
+
+               /* get complete attribute length and check for roque values */
+               a_l = udf_rw32(attrhdr->a_l);
+               DPRINTF(EXTATTR, ("attribute %d:%d, len %d/%d\n",
+                               udf_rw32(attrhdr->type),
+                               attrhdr->subtype, a_l, l_ea));
+               if ((a_l == 0) || (a_l > l_ea))
+                       return EINVAL;
+
+               if (attrhdr->type != sattr)
+                       goto next_attribute;
+
+               /* we might have found it! */
+               if (attrhdr->type < 2048) {     /* Ecma-167 attribute */
+                       *offsetp = offset;
+                       *lengthp = a_l;
+                       return 0;               /* success */
+               }
+
+               /*
+                * Implementation use and application use extended attributes
+                * have a name to identify. They share the same structure only
+                * UDF implementation use extended attributes have a checksum
+                * we need to check
+                */
+
+               DPRINTF(EXTATTR, ("named attribute %s\n", implext->imp_id.id));
+               if (strcmp(implext->imp_id.id, sattrname) == 0) {
+                       /* we have found our appl/implementation attribute */
+                       *offsetp = offset;
+                       *lengthp = a_l;
+                       return 0;               /* success */
+               }
+
+next_attribute:
+               /* next attribute */
+               pos    += a_l;
+               l_ea   -= a_l;
+               offset += a_l;
+       }
+       /* not found */
+       return ENOENT;
+}
+
+
+static void
+udf_extattr_insert_internal(struct udf_mount *ump, union dscrptr *dscr,
+       struct extattr_entry *extattr)
+{
+       struct file_entry      *fe;
+       struct extfile_entry   *efe;
+       struct extattrhdr_desc *extattrhdr;
+       struct impl_extattr_entry *implext;
+       uint32_t impl_attr_loc, appl_attr_loc, l_ea, a_l, exthdr_len;
+       uint32_t *l_eap, l_ad;
+       uint16_t *spos;
+       uint8_t *bpos, *data;
+
+       if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
+               fe    = &dscr->fe;
+               data  = fe->data;
+               l_eap = &fe->l_ea;
+               l_ad  = udf_rw32(fe->l_ad);
+       } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
+               efe   = &dscr->efe;
+               data  = efe->data;
+               l_eap = &efe->l_ea;
+               l_ad  = udf_rw32(efe->l_ad);
+       } else {
+               panic("Bad tag passed to udf_extattr_insert_internal");
+       }
+
+       /* can't append already written to file descriptors yet */
+       assert(l_ad == 0);
+
+       /* should have a header! */
+       extattrhdr = (struct extattrhdr_desc *) data;
+       l_ea = udf_rw32(*l_eap);
+       if (l_ea == 0) {
+               /* create empty extended attribute header */
+               exthdr_len = sizeof(struct extattrhdr_desc);
+
+               udf_inittag(ump, &extattrhdr->tag, TAGID_EXTATTR_HDR,
+                       /* loc */ 0);
+               extattrhdr->impl_attr_loc = udf_rw32(exthdr_len);
+               extattrhdr->appl_attr_loc = udf_rw32(exthdr_len);
+               extattrhdr->tag.desc_crc_len = udf_rw16(8);
+
+               /* record extended attribute header length */
+               l_ea = exthdr_len;
+               *l_eap = udf_rw32(l_ea);
+       }
+
+       /* extract locations */
+       impl_attr_loc = udf_rw32(extattrhdr->impl_attr_loc);
+       appl_attr_loc = udf_rw32(extattrhdr->appl_attr_loc);
+       if (impl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT)
+               impl_attr_loc = l_ea;
+       if (appl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT)
+               appl_attr_loc = l_ea;
+
+       /* Ecma 167 EAs */
+       if (udf_rw32(extattr->type) < 2048) {
+               assert(impl_attr_loc == l_ea);
+               assert(appl_attr_loc == l_ea);
+       }
+
+       /* implementation use extended attributes */
+       if (udf_rw32(extattr->type) == 2048) {
+               assert(appl_attr_loc == l_ea);
+
+               /* calculate and write extended attribute header checksum */
+               implext = (struct impl_extattr_entry *) extattr;
+               assert(udf_rw32(implext->iu_l) == 4);   /* [UDF 3.3.4.5] */
+               spos = (uint16_t *) implext->data;
+               *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext));
+       }
+
+       /* application use extended attributes */
+       assert(udf_rw32(extattr->type) != 65536);
+       assert(appl_attr_loc == l_ea);
+
+       /* append the attribute at the end of the current space */
+       bpos = data + udf_rw32(*l_eap);
+       a_l  = udf_rw32(extattr->a_l);
+
+       /* update impl. attribute locations */
+       if (udf_rw32(extattr->type) < 2048) {
+               impl_attr_loc = l_ea + a_l;
+               appl_attr_loc = l_ea + a_l;
+       }
+       if (udf_rw32(extattr->type) == 2048) {
+               appl_attr_loc = l_ea + a_l;
+       }
+
+       /* copy and advance */
+       memcpy(bpos, extattr, a_l);
+       l_ea += a_l;
+       *l_eap = udf_rw32(l_ea);
+
+       /* do the `dance` again backwards */
+       if (udf_rw16(ump->logical_vol->tag.descriptor_ver) != 2) {
+               if (impl_attr_loc == l_ea)
+                       impl_attr_loc = UDF_IMPL_ATTR_LOC_NOT_PRESENT;
+               if (appl_attr_loc == l_ea)
+                       appl_attr_loc = UDF_APPL_ATTR_LOC_NOT_PRESENT;
+       }
+
+       /* store offsets */
+       extattrhdr->impl_attr_loc = udf_rw32(impl_attr_loc);
+       extattrhdr->appl_attr_loc = udf_rw32(appl_attr_loc);
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static int 
+udf_update_lvid_from_vat_extattr(struct udf_node *vat_node)
+{
+       struct udf_mount       *ump;
+       struct udf_logvol_info *lvinfo;
+       struct impl_extattr_entry     *implext;
+       struct vatlvext_extattr_entry  lvext;
+       const char *extstr = "*UDF VAT LVExtension";
+       uint64_t    vat_uniqueid;
+       uint32_t    offset, a_l;
+       uint8_t    *ea_start, *lvextpos;
+       int         error;
+
+       /* get mountpoint and lvinfo */
+       ump    = vat_node->ump;
+       lvinfo = ump->logvol_info;
+
+       /* get information from fe/efe */
+       if (vat_node->fe) {
+               vat_uniqueid = udf_rw64(vat_node->fe->unique_id);
+               ea_start     = vat_node->fe->data;
+       } else {
+               vat_uniqueid = udf_rw64(vat_node->efe->unique_id);
+               ea_start     = vat_node->efe->data;
+       }
+
+       error = udf_extattr_search_intern(vat_node, 2048, extstr, &offset, &a_l);
+       if (error)
+               return error;
+
+       implext = (struct impl_extattr_entry *) (ea_start + offset);
+       error = udf_impl_extattr_check(implext);
+       if (error)
+               return error;
+
+       /* paranoia */
+       if (a_l != sizeof(*implext) -1 + udf_rw32(implext->iu_l) + sizeof(lvext)) {
+               DPRINTF(VOLUMES, ("VAT LVExtension size doesn't compute\n"));
+               return EINVAL;
+       }
+
+       /*
+        * we have found our "VAT LVExtension attribute. BUT due to a
+        * bug in the specification it might not be word aligned so
+        * copy first to avoid panics on some machines (!!)
+        */
+       DPRINTF(VOLUMES, ("Found VAT LVExtension attr\n"));
+       lvextpos = implext->data + udf_rw32(implext->iu_l);
+       memcpy(&lvext, lvextpos, sizeof(lvext));
+
+       /* check if it was updated the last time */
+       if (udf_rw64(lvext.unique_id_chk) == vat_uniqueid) {
+               lvinfo->num_files       = lvext.num_files;
+               lvinfo->num_directories = lvext.num_directories;
+               udf_update_logvolname(ump, lvext.logvol_id);
+       } else {
+               DPRINTF(VOLUMES, ("VAT LVExtension out of date\n"));
+               /* replace VAT LVExt by free space EA */
+               memset(implext->imp_id.id, 0, UDF_REGID_ID_SIZE);
+               strcpy(implext->imp_id.id, "*UDF FreeEASpace");
+               udf_calc_impl_extattr_checksum(implext);
+       }
+
+       return 0;
+}
+
+
+static int 
+udf_update_vat_extattr_from_lvid(struct udf_node *vat_node)
+{
+       struct udf_mount       *ump;
+       struct udf_logvol_info *lvinfo;
+       struct impl_extattr_entry     *implext;
+       struct vatlvext_extattr_entry  lvext;
+       const char *extstr = "*UDF VAT LVExtension";
+       uint64_t    vat_uniqueid;
+       uint32_t    offset, a_l;
+       uint8_t    *ea_start, *lvextpos;
+       int         error;
+
+       /* get mountpoint and lvinfo */
+       ump    = vat_node->ump;
+       lvinfo = ump->logvol_info;
+
+       /* get information from fe/efe */
+       if (vat_node->fe) {
+               vat_uniqueid = udf_rw64(vat_node->fe->unique_id);
+               ea_start     = vat_node->fe->data;
+       } else {
+               vat_uniqueid = udf_rw64(vat_node->efe->unique_id);
+               ea_start     = vat_node->efe->data;
+       }
+
+       error = udf_extattr_search_intern(vat_node, 2048, extstr, &offset, &a_l);
+       if (error)
+               return error;
+       /* found, it existed */
+
+       /* paranoia */
+       implext = (struct impl_extattr_entry *) (ea_start + offset);
+       error = udf_impl_extattr_check(implext);
+       if (error) {
+               DPRINTF(VOLUMES, ("VAT LVExtension bad on update\n"));
+               return error;
+       }
+       /* it is correct */
+
+       /*
+        * we have found our "VAT LVExtension attribute. BUT due to a
+        * bug in the specification it might not be word aligned so
+        * copy first to avoid panics on some machines (!!)
+        */
+       DPRINTF(VOLUMES, ("Updating VAT LVExtension attr\n"));
+       lvextpos = implext->data + udf_rw32(implext->iu_l);
+
+       lvext.unique_id_chk   = vat_uniqueid;
+       lvext.num_files       = lvinfo->num_files;
+       lvext.num_directories = lvinfo->num_directories;
+       memmove(lvext.logvol_id, ump->logical_vol->logvol_id, 128);
+
+       memcpy(lvextpos, &lvext, sizeof(lvext));
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_vat_read(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset)
+{
+       struct udf_mount *ump = vat_node->ump;
+
+       if (offset + size > ump->vat_offset + ump->vat_entries * 4)
+               return EINVAL;
+
+       memcpy(blob, ump->vat_table + offset, size);
+       return 0;
+}
+
+int
+udf_vat_write(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset)
+{
+       struct udf_mount *ump = vat_node->ump;
+       uint32_t offset_high;
+       uint8_t *new_vat_table;
+
+       /* extent VAT allocation if needed */
+       offset_high = offset + size;
+       if (offset_high >= ump->vat_table_alloc_len) {
+               /* realloc */
+               new_vat_table = realloc(ump->vat_table,
+                       ump->vat_table_alloc_len + UDF_VAT_CHUNKSIZE,
+                       M_UDFVOLD, M_WAITOK | M_CANFAIL);
+               if (!new_vat_table) {
+                       printf("udf_vat_write: can't extent VAT, out of mem\n");
+                       return ENOMEM;
+               }
+               ump->vat_table = new_vat_table;
+               ump->vat_table_alloc_len += UDF_VAT_CHUNKSIZE;
+       }
+       ump->vat_table_len = MAX(ump->vat_table_len, offset_high);
+
+       memcpy(ump->vat_table + offset, blob, size);
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* TODO support previous VAT location writeout */
+static int
+udf_update_vat_descriptor(struct udf_mount *ump)
+{
+       struct udf_node *vat_node = ump->vat_node;
+       struct udf_logvol_info *lvinfo = ump->logvol_info;
+       struct icb_tag *icbtag;
+       struct udf_oldvat_tail *oldvat_tl;
+       struct udf_vat *vat;
+       uint64_t unique_id;
+       uint32_t lb_size;
+       uint8_t *raw_vat;
+       int filetype, error;
+
+       KASSERT(vat_node);
+       KASSERT(lvinfo);
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+
+       /* get our new unique_id */
+       unique_id = udf_advance_uniqueid(ump);
+
+       /* get information from fe/efe */
+       if (vat_node->fe) {
+               icbtag    = &vat_node->fe->icbtag;
+               vat_node->fe->unique_id = udf_rw64(unique_id);
+       } else {
+               icbtag = &vat_node->efe->icbtag;
+               vat_node->efe->unique_id = udf_rw64(unique_id);
+       }
+
+       /* Check icb filetype! it has to be 0 or UDF_ICB_FILETYPE_VAT */
+       filetype = icbtag->file_type;
+       KASSERT((filetype == 0) || (filetype == UDF_ICB_FILETYPE_VAT));
+
+       /* allocate piece to process head or tail of VAT file */
+       raw_vat = malloc(lb_size, M_TEMP, M_WAITOK);
+
+       if (filetype == 0) {
+               /*
+                * Update "*UDF VAT LVExtension" extended attribute from the
+                * lvint if present.
+                */
+               udf_update_vat_extattr_from_lvid(vat_node);
+
+               /* setup identifying regid */
+               oldvat_tl = (struct udf_oldvat_tail *) raw_vat;
+               memset(oldvat_tl, 0, sizeof(struct udf_oldvat_tail));
+
+               udf_set_regid(&oldvat_tl->id, "*UDF Virtual Alloc Tbl");
+               udf_add_udf_regid(ump, &oldvat_tl->id);
+               oldvat_tl->prev_vat = udf_rw32(0xffffffff);
+
+               /* write out new tail of virtual allocation table file */
+               error = udf_vat_write(vat_node, raw_vat,
+                       sizeof(struct udf_oldvat_tail), ump->vat_entries * 4);
+       } else {
+               /* compose the VAT2 header */
+               vat = (struct udf_vat *) raw_vat;
+               memset(vat, 0, sizeof(struct udf_vat));
+
+               vat->header_len       = udf_rw16(152);  /* as per spec */
+               vat->impl_use_len     = udf_rw16(0);
+               memmove(vat->logvol_id, ump->logical_vol->logvol_id, 128);
+               vat->prev_vat         = udf_rw32(0xffffffff);
+               vat->num_files        = lvinfo->num_files;
+               vat->num_directories  = lvinfo->num_directories;
+               vat->min_udf_readver  = lvinfo->min_udf_readver;
+               vat->min_udf_writever = lvinfo->min_udf_writever;
+               vat->max_udf_writever = lvinfo->max_udf_writever;
+
+               error = udf_vat_write(vat_node, raw_vat,
+                       sizeof(struct udf_vat), 0);
+       }
+       free(raw_vat, M_TEMP);
+
+       return error;   /* success! */
+}
+
+
+int
+udf_writeout_vat(struct udf_mount *ump)
+{
+       struct udf_node *vat_node = ump->vat_node;
+       int error;
+
+       KASSERT(vat_node);
+
+       DPRINTF(CALL, ("udf_writeout_vat\n"));
+
+//     mutex_enter(&ump->allocate_mutex);
+       udf_update_vat_descriptor(ump);
+
+       /* write out the VAT contents ; TODO intelligent writing */
+       error = vn_rdwr(UIO_WRITE, vat_node->vnode,
+               ump->vat_table, ump->vat_table_len, 0,
+               UIO_SYSSPACE, 0, FSCRED, NULL, NULL);
+       if (error) {
+               printf("udf_writeout_vat: failed to write out VAT contents\n");
+               goto out;
+       }
+
+//     mutex_exit(&ump->allocate_mutex);
+
+       error = vflushbuf(ump->vat_node->vnode, FSYNC_WAIT);
+       if (error)
+               goto out;
+       error = VOP_FSYNC(ump->vat_node->vnode,
+                       FSCRED, FSYNC_WAIT, 0, 0);
+       if (error)
+               printf("udf_writeout_vat: error writing VAT node!\n");
+out:
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Read in relevant pieces of VAT file and check if its indeed a VAT file
+ * descriptor. If OK, read in complete VAT file.
+ */
+
+static int
+udf_check_for_vat(struct udf_node *vat_node)
+{
+       struct udf_mount *ump;
+       struct icb_tag   *icbtag;
+       struct timestamp *mtime;
+       struct udf_vat   *vat;
+       struct udf_oldvat_tail *oldvat_tl;
+       struct udf_logvol_info *lvinfo;
+       uint64_t  unique_id;
+       uint32_t  vat_length;
+       uint32_t  vat_offset, vat_entries, vat_table_alloc_len;
+       uint32_t  sector_size;
+       uint32_t *raw_vat;
+       uint8_t  *vat_table;
+       char     *regid_name;
+       int filetype;
+       int error;
+
+       /* vat_length is really 64 bits though impossible */
+
+       DPRINTF(VOLUMES, ("Checking for VAT\n"));
+       if (!vat_node)
+               return ENOENT;
+
+       /* get mount info */
+       ump = vat_node->ump;
+       sector_size = udf_rw32(ump->logical_vol->lb_size);
+
+       /* check assertions */
+       assert(vat_node->fe || vat_node->efe);
+       assert(ump->logvol_integrity);
+
+       /* set vnode type to regular file or we can't read from it! */
+       vat_node->vnode->v_type = VREG;
+
+       /* get information from fe/efe */
+       if (vat_node->fe) {
+               vat_length = udf_rw64(vat_node->fe->inf_len);
+               icbtag    = &vat_node->fe->icbtag;
+               mtime     = &vat_node->fe->mtime;
+               unique_id = udf_rw64(vat_node->fe->unique_id);
+       } else {
+               vat_length = udf_rw64(vat_node->efe->inf_len);
+               icbtag = &vat_node->efe->icbtag;
+               mtime  = &vat_node->efe->mtime;
+               unique_id = udf_rw64(vat_node->efe->unique_id);
+       }
+
+       /* Check icb filetype! it has to be 0 or UDF_ICB_FILETYPE_VAT */
+       filetype = icbtag->file_type;
+       if ((filetype != 0) && (filetype != UDF_ICB_FILETYPE_VAT))
+               return ENOENT;
+
+       DPRINTF(VOLUMES, ("\tPossible VAT length %d\n", vat_length));
+
+       vat_table_alloc_len =
+               ((vat_length + UDF_VAT_CHUNKSIZE-1) / UDF_VAT_CHUNKSIZE)
+                       * UDF_VAT_CHUNKSIZE;
+
+       vat_table = malloc(vat_table_alloc_len, M_UDFVOLD,
+               M_CANFAIL | M_WAITOK);
+       if (vat_table == NULL) {
+               printf("allocation of %d bytes failed for VAT\n",
+                       vat_table_alloc_len);
+               return ENOMEM;
+       }
+
+       /* allocate piece to read in head or tail of VAT file */
+       raw_vat = malloc(sector_size, M_TEMP, M_WAITOK);
+
+       /*
+        * check contents of the file if its the old 1.50 VAT table format.
+        * Its notoriously broken and allthough some implementations support an
+        * extention as defined in the UDF 1.50 errata document, its doubtfull
+        * to be useable since a lot of implementations don't maintain it.
+        */
+       lvinfo = ump->logvol_info;
+
+       if (filetype == 0) {
+               /* definition */
+               vat_offset  = 0;
+               vat_entries = (vat_length-36)/4;
+
+               /* read in tail of virtual allocation table file */
+               error = vn_rdwr(UIO_READ, vat_node->vnode,
+                               (uint8_t *) raw_vat,
+                               sizeof(struct udf_oldvat_tail),
+                               vat_entries * 4,
+                               UIO_SYSSPACE, IO_SYNC | IO_NODELOCKED, FSCRED,
+                               NULL, NULL);
+               if (error)
+                       goto out;
+
+               /* check 1.50 VAT */
+               oldvat_tl = (struct udf_oldvat_tail *) raw_vat;
+               regid_name = (char *) oldvat_tl->id.id;
+               error = strncmp(regid_name, "*UDF Virtual Alloc Tbl", 22);
+               if (error) {
+                       DPRINTF(VOLUMES, ("VAT format 1.50 rejected\n"));
+                       error = ENOENT;
+                       goto out;
+               }
+
+               /*
+                * update LVID from "*UDF VAT LVExtension" extended attribute
+                * if present.
+                */
+               udf_update_lvid_from_vat_extattr(vat_node);
+       } else {
+               /* read in head of virtual allocation table file */
+               error = vn_rdwr(UIO_READ, vat_node->vnode,
+                               (uint8_t *) raw_vat,
+                               sizeof(struct udf_vat), 0,
+                               UIO_SYSSPACE, IO_SYNC | IO_NODELOCKED, FSCRED,
+                               NULL, NULL);
+               if (error)
+                       goto out;
+
+               /* definition */
+               vat = (struct udf_vat *) raw_vat;
+               vat_offset  = vat->header_len;
+               vat_entries = (vat_length - vat_offset)/4;
+
+               assert(lvinfo);
+               lvinfo->num_files        = vat->num_files;
+               lvinfo->num_directories  = vat->num_directories;
+               lvinfo->min_udf_readver  = vat->min_udf_readver;
+               lvinfo->min_udf_writever = vat->min_udf_writever;
+               lvinfo->max_udf_writever = vat->max_udf_writever;
+       
+               udf_update_logvolname(ump, vat->logvol_id);
+       }
+
+       /* read in complete VAT file */
+       error = vn_rdwr(UIO_READ, vat_node->vnode,
+                       vat_table,
+                       vat_length, 0,
+                       UIO_SYSSPACE, IO_SYNC | IO_NODELOCKED, FSCRED,
+                       NULL, NULL);
+       if (error)
+               printf("read in of complete VAT file failed (error %d)\n",
+                       error);
+       if (error)
+               goto out;
+
+       DPRINTF(VOLUMES, ("VAT format accepted, marking it closed\n"));
+       ump->logvol_integrity->lvint_next_unique_id = udf_rw64(unique_id);
+       ump->logvol_integrity->integrity_type = udf_rw32(UDF_INTEGRITY_CLOSED);
+       ump->logvol_integrity->time           = *mtime;
+
+       ump->vat_table_len = vat_length;
+       ump->vat_table_alloc_len = vat_table_alloc_len;
+       ump->vat_table   = vat_table;
+       ump->vat_offset  = vat_offset;
+       ump->vat_entries = vat_entries;
+       ump->vat_last_free_lb = 0;              /* start at beginning */
+
+out:
+       if (error) {
+               if (vat_table)
+                       free(vat_table, M_UDFVOLD);
+       }
+       free(raw_vat, M_TEMP);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_search_vat(struct udf_mount *ump, union udf_pmap *mapping)
+{
+       struct udf_node *vat_node;
+       struct long_ad   icb_loc;
+       uint32_t early_vat_loc, vat_loc;
+       int error;
+
+       /* mapping info not needed */
+       mapping = mapping;
+
+       vat_loc = ump->last_possible_vat_location;
+       early_vat_loc = vat_loc - 256;  /* 8 blocks of 32 sectors */
+
+       DPRINTF(VOLUMES, ("1) last possible %d, early_vat_loc %d \n",
+               vat_loc, early_vat_loc));
+       early_vat_loc = MAX(early_vat_loc, ump->first_possible_vat_location);
+
+       DPRINTF(VOLUMES, ("2) last possible %d, early_vat_loc %d \n",
+               vat_loc, early_vat_loc));
+
+       /* start looking from the end of the range */
+       do {
+               DPRINTF(VOLUMES, ("Checking for VAT at sector %d\n", vat_loc));
+               icb_loc.loc.part_num = udf_rw16(UDF_VTOP_RAWPART);
+               icb_loc.loc.lb_num   = udf_rw32(vat_loc);
+
+               error = udf_get_node(ump, &icb_loc, &vat_node);
+               if (!error) {
+                       error = udf_check_for_vat(vat_node);
+                       DPRINTFIF(VOLUMES, !error,
+                               ("VAT accepted at %d\n", vat_loc));
+                       if (!error)
+                               break;
+               }
+               if (vat_node) {
+                       vput(vat_node->vnode);
+                       vat_node = NULL;
+               }
+               vat_loc--;      /* walk backwards */
+       } while (vat_loc >= early_vat_loc);
+
+       /* keep our VAT node around */
+       if (vat_node) {
+               UDF_SET_SYSTEMFILE(vat_node->vnode);
+               ump->vat_node = vat_node;
+       }
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_read_sparables(struct udf_mount *ump, union udf_pmap *mapping)
+{
+       union dscrptr *dscr;
+       struct part_map_spare *pms = &mapping->pms;
+       uint32_t lb_num;
+       int spar, error;
+
+       /*
+        * The partition mapping passed on to us specifies the information we
+        * need to locate and initialise the sparable partition mapping
+        * information we need.
+        */
+
+       DPRINTF(VOLUMES, ("Read sparable table\n"));
+       ump->sparable_packet_size = udf_rw16(pms->packet_len);
+       KASSERT(ump->sparable_packet_size >= ump->packet_size); /* XXX */
+
+       for (spar = 0; spar < pms->n_st; spar++) {
+               lb_num = pms->st_loc[spar];
+               DPRINTF(VOLUMES, ("Checking for sparing table %d\n", lb_num));
+               error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr);
+               if (!error && dscr) {
+                       if (udf_rw16(dscr->tag.id) == TAGID_SPARING_TABLE) {
+                               if (ump->sparing_table)
+                                       free(ump->sparing_table, M_UDFVOLD);
+                               ump->sparing_table = &dscr->spt;
+                               dscr = NULL;
+                               DPRINTF(VOLUMES,
+                                   ("Sparing table accepted (%d entries)\n",
+                                    udf_rw16(ump->sparing_table->rt_l)));
+                               break;  /* we're done */
+                       }
+               }
+               if (dscr)
+                       free(dscr, M_UDFVOLD);
+       }
+
+       if (ump->sparing_table)
+               return 0;
+
+       return ENOENT;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_read_metadata_nodes(struct udf_mount *ump, union udf_pmap *mapping)
+{
+       struct part_map_meta *pmm = &mapping->pmm;
+       struct long_ad   icb_loc;
+       struct vnode *vp;
+       uint16_t raw_phys_part, phys_part;
+       int error;
+
+       /*
+        * BUGALERT: some rogue implementations use random physical
+        * partition numbers to break other implementations so lookup
+        * the number.
+        */
+
+       /* extract our allocation parameters set up on format */
+       ump->metadata_alloc_unit_size     = udf_rw32(mapping->pmm.alloc_unit_size);
+       ump->metadata_alignment_unit_size = udf_rw16(mapping->pmm.alignment_unit_size);
+       ump->metadata_flags = mapping->pmm.flags;
+
+       DPRINTF(VOLUMES, ("Reading in Metadata files\n"));
+       raw_phys_part = udf_rw16(pmm->part_num);
+       phys_part = udf_find_raw_phys(ump, raw_phys_part);
+
+       icb_loc.loc.part_num = udf_rw16(phys_part);
+
+       DPRINTF(VOLUMES, ("Metadata file\n"));
+       icb_loc.loc.lb_num   = pmm->meta_file_lbn;
+       error = udf_get_node(ump, &icb_loc, &ump->metadata_node);
+       if (ump->metadata_node) {
+               vp = ump->metadata_node->vnode;
+               UDF_SET_SYSTEMFILE(vp);
+       }
+
+       icb_loc.loc.lb_num   = pmm->meta_mirror_file_lbn;
+       if (icb_loc.loc.lb_num != -1) {
+               DPRINTF(VOLUMES, ("Metadata copy file\n"));
+               error = udf_get_node(ump, &icb_loc, &ump->metadatamirror_node);
+               if (ump->metadatamirror_node) {
+                       vp = ump->metadatamirror_node->vnode;
+                       UDF_SET_SYSTEMFILE(vp);
+               }
+       }
+
+       icb_loc.loc.lb_num   = pmm->meta_bitmap_file_lbn;
+       if (icb_loc.loc.lb_num != -1) {
+               DPRINTF(VOLUMES, ("Metadata bitmap file\n"));
+               error = udf_get_node(ump, &icb_loc, &ump->metadatabitmap_node);
+               if (ump->metadatabitmap_node) {
+                       vp = ump->metadatabitmap_node->vnode;
+                       UDF_SET_SYSTEMFILE(vp);
+               }
+       }
+
+       /* if we're mounting read-only we relax the requirements */
+       if (ump->vfs_mountp->mnt_flag & MNT_RDONLY) {
+               error = EFAULT;
+               if (ump->metadata_node)
+                       error = 0;
+               if ((ump->metadata_node == NULL) && (ump->metadatamirror_node)) {
+                       printf( "udf mount: Metadata file not readable, "
+                               "substituting Metadata copy file\n");
+                       ump->metadata_node = ump->metadatamirror_node;
+                       ump->metadatamirror_node = NULL;
+                       error = 0;
+               }
+       } else {
+               /* mounting read/write */
+               /* XXX DISABLED! metadata writing is not working yet XXX */
+               if (error)
+                       error = EROFS;
+       }
+       DPRINTFIF(VOLUMES, error, ("udf mount: failed to read "
+                                  "metadata files\n"));
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_read_vds_tables(struct udf_mount *ump)
+{
+       union udf_pmap *mapping;
+       /* struct udf_args *args = &ump->mount_args; */
+       uint32_t n_pm;
+       uint32_t log_part;
+       uint8_t *pmap_pos;
+       int pmap_size;
+       int error;
+
+       /* Iterate (again) over the part mappings for locations   */
+       n_pm = udf_rw32(ump->logical_vol->n_pm);   /* num partmaps         */
+       pmap_pos =  ump->logical_vol->maps;
+
+       for (log_part = 0; log_part < n_pm; log_part++) {
+               mapping = (union udf_pmap *) pmap_pos;
+               switch (ump->vtop_tp[log_part]) {
+               case UDF_VTOP_TYPE_PHYS :
+                       /* nothing */
+                       break;
+               case UDF_VTOP_TYPE_VIRT :
+                       /* search and load VAT */
+                       error = udf_search_vat(ump, mapping);
+                       if (error)
+                               return ENOENT;
+                       break;
+               case UDF_VTOP_TYPE_SPARABLE :
+                       /* load one of the sparable tables */
+                       error = udf_read_sparables(ump, mapping);
+                       if (error)
+                               return ENOENT;
+                       break;
+               case UDF_VTOP_TYPE_META :
+                       /* load the associated file descriptors */
+                       error = udf_read_metadata_nodes(ump, mapping);
+                       if (error)
+                               return ENOENT;
+                       break;
+               default:
+                       break;
+               }
+               pmap_size  = pmap_pos[1];
+               pmap_pos  += pmap_size;
+       }
+
+       /* read in and check unallocated and free space info if writing */
+       if ((ump->vfs_mountp->mnt_flag & MNT_RDONLY) == 0) {
+               error = udf_read_physical_partition_spacetables(ump);
+               if (error)
+                       return error;
+
+               /* also read in metadata partition spacebitmap if defined */
+               error = udf_read_metadata_partition_spacetable(ump);
+                       return error;
+       }
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_read_rootdirs(struct udf_mount *ump)
+{
+       union dscrptr *dscr;
+       /* struct udf_args *args = &ump->mount_args; */
+       struct udf_node *rootdir_node, *streamdir_node;
+       struct long_ad  fsd_loc, *dir_loc;
+       uint32_t lb_num, dummy;
+       uint32_t fsd_len;
+       int dscr_type;
+       int error;
+
+       /* TODO implement FSD reading in separate function like integrity? */
+       /* get fileset descriptor sequence */
+       fsd_loc = ump->logical_vol->lv_fsd_loc;
+       fsd_len = udf_rw32(fsd_loc.len);
+
+       dscr  = NULL;
+       error = 0;
+       while (fsd_len || error) {
+               DPRINTF(VOLUMES, ("fsd_len = %d\n", fsd_len));
+               /* translate fsd_loc to lb_num */
+               error = udf_translate_vtop(ump, &fsd_loc, &lb_num, &dummy);
+               if (error)
+                       break;
+               DPRINTF(VOLUMES, ("Reading FSD at lb %d\n", lb_num));
+               error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr);
+               /* end markers */
+               if (error || (dscr == NULL))
+                       break;
+
+               /* analyse */
+               dscr_type = udf_rw16(dscr->tag.id);
+               if (dscr_type == TAGID_TERM)
+                       break;
+               if (dscr_type != TAGID_FSD) {
+                       free(dscr, M_UDFVOLD);
+                       return ENOENT;
+               }
+
+               /*
+                * TODO check for multiple fileset descriptors; its only
+                * picking the last now. Also check for FSD
+                * correctness/interpretability
+                */
+
+               /* update */
+               if (ump->fileset_desc) {
+                       free(ump->fileset_desc, M_UDFVOLD);
+               }
+               ump->fileset_desc = &dscr->fsd;
+               dscr = NULL;
+
+               /* continue to the next fsd */
+               fsd_len -= ump->discinfo.sector_size;
+               fsd_loc.loc.lb_num = udf_rw32(udf_rw32(fsd_loc.loc.lb_num)+1);
+
+               /* follow up to fsd->next_ex (long_ad) if its not null */
+               if (udf_rw32(ump->fileset_desc->next_ex.len)) {
+                       DPRINTF(VOLUMES, ("follow up FSD extent\n"));
+                       fsd_loc = ump->fileset_desc->next_ex;
+                       fsd_len = udf_rw32(ump->fileset_desc->next_ex.len);
+               }
+       }
+       if (dscr)
+               free(dscr, M_UDFVOLD);
+
+       /* there has to be one */
+       if (ump->fileset_desc == NULL)
+               return ENOENT;
+
+       DPRINTF(VOLUMES, ("FSD read in fine\n"));
+       DPRINTF(VOLUMES, ("Updating fsd logical volume id\n"));
+       udf_update_logvolname(ump, ump->logical_vol->logvol_id);
+
+       /*
+        * Now the FSD is known, read in the rootdirectory and if one exists,
+        * the system stream dir. Some files in the system streamdir are not
+        * wanted in this implementation since they are not maintained. If
+        * writing is enabled we'll delete these files if they exist.
+        */
+
+       rootdir_node = streamdir_node = NULL;
+       dir_loc = NULL;
+
+       /* try to read in the rootdir */
+       dir_loc = &ump->fileset_desc->rootdir_icb;
+       error = udf_get_node(ump, dir_loc, &rootdir_node);
+       if (error)
+               return ENOENT;
+
+       /* aparently it read in fine */
+
+       /*
+        * Try the system stream directory; not very likely in the ones we
+        * test, but for completeness.
+        */
+       dir_loc = &ump->fileset_desc->streamdir_icb;
+       if (udf_rw32(dir_loc->len)) {
+               printf("udf_read_rootdirs: streamdir defined ");
+               error = udf_get_node(ump, dir_loc, &streamdir_node);
+               if (error) {
+                       printf("but error in streamdir reading\n");
+               } else {
+                       printf("but ignored\n");
+                       /*
+                        * TODO process streamdir `baddies' i.e. files we dont
+                        * want if R/W
+                        */
+               }
+       }
+
+       DPRINTF(VOLUMES, ("Rootdir(s) read in fine\n"));
+
+       /* release the vnodes again; they'll be auto-recycled later */
+       if (streamdir_node) {
+               vput(streamdir_node->vnode);
+       }
+       if (rootdir_node) {
+               vput(rootdir_node->vnode);
+       }
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* To make absolutely sure we are NOT returning zero, add one :) */
+
+long
+udf_get_node_id(const struct long_ad *icbptr)
+{
+       /* ought to be enough since each mountpoint has its own chain */
+       return udf_rw32(icbptr->loc.lb_num) + 1;
+}
+
+
+int
+udf_compare_icb(const struct long_ad *a, const struct long_ad *b)
+{
+       if (udf_rw16(a->loc.part_num) < udf_rw16(b->loc.part_num))
+               return -1;
+       if (udf_rw16(a->loc.part_num) > udf_rw16(b->loc.part_num))
+               return 1;
+
+       if (udf_rw32(a->loc.lb_num) < udf_rw32(b->loc.lb_num))
+               return -1;
+       if (udf_rw32(a->loc.lb_num) > udf_rw32(b->loc.lb_num))
+               return 1;
+
+       return 0;
+}
+
+
+static int
+udf_compare_rbnodes(void *ctx, const void *a, const void *b)
+{
+       const struct udf_node *a_node = a;
+       const struct udf_node *b_node = b;
+
+       return udf_compare_icb(&a_node->loc, &b_node->loc);
+}
+
+
+static int
+udf_compare_rbnode_icb(void *ctx, const void *a, const void *key)
+{
+       const struct udf_node *a_node = a;
+       const struct long_ad * const icb = key;
+
+       return udf_compare_icb(&a_node->loc, icb);
+}
+
+
+static const rb_tree_ops_t udf_node_rbtree_ops = {
+       .rbto_compare_nodes = udf_compare_rbnodes,
+       .rbto_compare_key = udf_compare_rbnode_icb,
+       .rbto_node_offset = offsetof(struct udf_node, rbnode),
+       .rbto_context = NULL
+};
+
+
+void
+udf_init_nodes_tree(struct udf_mount *ump)
+{
+
+       rb_tree_init(&ump->udf_node_tree, &udf_node_rbtree_ops);
+}
+
+
+static struct udf_node *
+udf_node_lookup(struct udf_mount *ump, struct long_ad *icbptr)
+{
+       struct udf_node *udf_node;
+       struct vnode *vp;
+
+loop:
+       mutex_enter(&ump->ihash_lock);
+
+       udf_node = rb_tree_find_node(&ump->udf_node_tree, icbptr);
+       if (udf_node) {
+               vp = udf_node->vnode;
+               assert(vp);
+               mutex_enter(vp->v_interlock);
+               mutex_exit(&ump->ihash_lock);
+               if (vget(vp, LK_EXCLUSIVE))
+                       goto loop;
+               return udf_node;
+       }
+       mutex_exit(&ump->ihash_lock);
+
+       return NULL;
+}
+
+
+static void
+udf_register_node(struct udf_node *udf_node)
+{
+       struct udf_mount *ump = udf_node->ump;
+
+       /* add node to the rb tree */
+       mutex_enter(&ump->ihash_lock);
+       rb_tree_insert_node(&ump->udf_node_tree, udf_node);
+       mutex_exit(&ump->ihash_lock);
+}
+
+
+static void
+udf_deregister_node(struct udf_node *udf_node) 
+{
+       struct udf_mount *ump = udf_node->ump;
+
+       /* remove node from the rb tree */
+       mutex_enter(&ump->ihash_lock);
+       rb_tree_remove_node(&ump->udf_node_tree, udf_node);
+       mutex_exit(&ump->ihash_lock);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_validate_session_start(struct udf_mount *ump)
+{
+       struct mmc_trackinfo trackinfo;
+       struct vrs_desc *vrs;
+       uint32_t tracknr, sessionnr, sector, sector_size;
+       uint32_t iso9660_vrs, write_track_start;
+       uint8_t *buffer, *blank, *pos;
+       int blks, max_sectors, vrs_len;
+       int error;
+
+       /* disc appendable? */
+       if (ump->discinfo.disc_state == MMC_STATE_FULL)
+               return EROFS;
+
+       /* already written here? if so, there should be an ISO VDS */
+       if (ump->discinfo.last_session_state == MMC_STATE_INCOMPLETE)
+               return 0;
+
+       /*
+        * Check if the first track of the session is blank and if so, copy or
+        * create a dummy ISO descriptor so the disc is valid again.
+        */
+
+       tracknr = ump->discinfo.first_track_last_session;
+       memset(&trackinfo, 0, sizeof(struct mmc_trackinfo));
+       trackinfo.tracknr = tracknr;
+       error = udf_update_trackinfo(ump, &trackinfo);
+       if (error)
+               return error;
+
+       udf_dump_trackinfo(&trackinfo);
+       KASSERT(trackinfo.flags & (MMC_TRACKINFO_BLANK | MMC_TRACKINFO_RESERVED));
+       KASSERT(trackinfo.sessionnr > 1);
+
+       KASSERT(trackinfo.flags & MMC_TRACKINFO_NWA_VALID);
+       write_track_start = trackinfo.next_writable;
+
+       /* we have to copy the ISO VRS from a former session */
+       DPRINTF(VOLUMES, ("validate_session_start: "
+                       "blank or reserved track, copying VRS\n"));
+
+       /* sessionnr should be the session we're mounting */
+       sessionnr = ump->mount_args.sessionnr;
+
+       /* start at the first track */
+       tracknr   = ump->discinfo.first_track;
+       while (tracknr <= ump->discinfo.num_tracks) {
+               trackinfo.tracknr = tracknr;
+               error = udf_update_trackinfo(ump, &trackinfo);
+               if (error) {
+                       DPRINTF(VOLUMES, ("failed to get trackinfo; aborting\n"));
+                       return error;
+               }
+               if (trackinfo.sessionnr == sessionnr)
+                       break;
+               tracknr++;
+       }
+       if (trackinfo.sessionnr != sessionnr) {
+               DPRINTF(VOLUMES, ("failed to get trackinfo; aborting\n"));
+               return ENOENT;
+       }
+
+       DPRINTF(VOLUMES, ("found possible former ISO VRS at\n"));
+       udf_dump_trackinfo(&trackinfo);
+
+        /*
+         * location of iso9660 vrs is defined as first sector AFTER 32kb,
+         * minimum ISO `sector size' 2048
+         */
+       sector_size = ump->discinfo.sector_size;
+       iso9660_vrs = ((32*1024 + sector_size - 1) / sector_size)
+                + trackinfo.track_start;
+
+       buffer = malloc(UDF_ISO_VRS_SIZE, M_TEMP, M_WAITOK);
+       max_sectors = UDF_ISO_VRS_SIZE / sector_size;
+       blks = MAX(1, 2048 / sector_size);
+
+       error = 0;
+       for (sector = 0; sector < max_sectors; sector += blks) {
+               pos = buffer + sector * sector_size;
+               error = udf_read_phys_sectors(ump, UDF_C_DSCR, pos,
+                       iso9660_vrs + sector, blks);
+               if (error)
+                       break;
+               /* check this ISO descriptor */
+               vrs = (struct vrs_desc *) pos;
+               DPRINTF(VOLUMES, ("got VRS id `%4s`\n", vrs->identifier));
+               if (strncmp(vrs->identifier, VRS_CD001, 5) == 0)
+                       continue;
+               if (strncmp(vrs->identifier, VRS_CDW02, 5) == 0)
+                       continue;
+               if (strncmp(vrs->identifier, VRS_BEA01, 5) == 0)
+                       continue;
+               if (strncmp(vrs->identifier, VRS_NSR02, 5) == 0)
+                       continue;
+               if (strncmp(vrs->identifier, VRS_NSR03, 5) == 0)
+                       continue;
+               if (strncmp(vrs->identifier, VRS_TEA01, 5) == 0)
+                       break;
+               /* now what? for now, end of sequence */
+               break;
+       }
+       vrs_len = sector + blks;
+       if (error) {
+               DPRINTF(VOLUMES, ("error reading old ISO VRS\n"));
+               DPRINTF(VOLUMES, ("creating minimal ISO VRS\n"));
+
+               memset(buffer, 0, UDF_ISO_VRS_SIZE);
+
+               vrs = (struct vrs_desc *) (buffer);
+               vrs->struct_type = 0;
+               vrs->version     = 1;
+               memcpy(vrs->identifier,VRS_BEA01, 5);
+
+               vrs = (struct vrs_desc *) (buffer + 2048);
+               vrs->struct_type = 0;
+               vrs->version     = 1;
+               if (udf_rw16(ump->logical_vol->tag.descriptor_ver) == 2) {
+                       memcpy(vrs->identifier,VRS_NSR02, 5);
+               } else {
+                       memcpy(vrs->identifier,VRS_NSR03, 5);
+               }
+
+               vrs = (struct vrs_desc *) (buffer + 4096);
+               vrs->struct_type = 0;
+               vrs->version     = 1;
+               memcpy(vrs->identifier, VRS_TEA01, 5);
+
+               vrs_len = 3*blks;
+       }
+
+       DPRINTF(VOLUMES, ("Got VRS of %d sectors long\n", vrs_len));
+
+        /*
+         * location of iso9660 vrs is defined as first sector AFTER 32kb,
+         * minimum ISO `sector size' 2048
+         */
+       sector_size = ump->discinfo.sector_size;
+       iso9660_vrs = ((32*1024 + sector_size - 1) / sector_size)
+                + write_track_start;
+
+       /* write out 32 kb */
+       blank = malloc(sector_size, M_TEMP, M_WAITOK);
+       memset(blank, 0, sector_size);
+       error = 0;
+       for (sector = write_track_start; sector < iso9660_vrs; sector ++) {
+               error = udf_write_phys_sectors(ump, UDF_C_ABSOLUTE,
+                       blank, sector, 1);
+               if (error)
+                       break;
+       }
+       if (!error) {
+               /* write out our ISO VRS */
+               KASSERT(sector == iso9660_vrs);
+               error = udf_write_phys_sectors(ump, UDF_C_ABSOLUTE, buffer,
+                               sector, vrs_len);
+               sector += vrs_len;
+       }
+       if (!error) {
+               /* fill upto the first anchor at S+256 */
+               for (; sector < write_track_start+256; sector++) {
+                       error = udf_write_phys_sectors(ump, UDF_C_ABSOLUTE,
+                               blank, sector, 1);
+                       if (error)
+                               break;
+               }
+       }
+       if (!error) {
+               /* write out anchor; write at ABSOLUTE place! */
+               error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_ABSOLUTE,
+                       (union dscrptr *) ump->anchors[0], sector, sector);
+               if (error)
+                       printf("writeout of anchor failed!\n");
+       }
+
+       free(blank, M_TEMP);
+       free(buffer, M_TEMP);
+
+       if (error)
+               printf("udf_open_session: error writing iso vrs! : "
+                               "leaving disc in compromised state!\n");
+
+       /* synchronise device caches */
+       (void) udf_synchronise_caches(ump);
+
+       return error;
+}
+
+
+int
+udf_open_logvol(struct udf_mount *ump)
+{
+       int logvol_integrity;
+       int error;
+
+       /* already/still open? */
+       logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type);
+       if (logvol_integrity == UDF_INTEGRITY_OPEN)
+               return 0;
+
+       /* can we open it ? */
+       if (ump->vfs_mountp->mnt_flag & MNT_RDONLY)
+               return EROFS;
+
+       /* setup write parameters */
+       DPRINTF(VOLUMES, ("Setting up write parameters\n"));
+       if ((error = udf_setup_writeparams(ump)) != 0)
+               return error;
+
+       /* determine data and metadata tracks (most likely same) */
+       error = udf_search_writing_tracks(ump);
+       if (error) {
+               /* most likely lack of space */
+               printf("udf_open_logvol: error searching writing tracks\n");
+               return EROFS;
+       }
+
+       /* writeout/update lvint on disc or only in memory */
+       DPRINTF(VOLUMES, ("Opening logical volume\n"));
+       if (ump->lvopen & UDF_OPEN_SESSION) {
+               /* TODO optional track reservation opening */
+               error = udf_validate_session_start(ump);
+               if (error)
+                       return error;
+
+               /* determine data and metadata tracks again */
+               error = udf_search_writing_tracks(ump);
+       }
+
+       /* mark it open */
+       ump->logvol_integrity->integrity_type = udf_rw32(UDF_INTEGRITY_OPEN);
+
+       /* do we need to write it out? */
+       if (ump->lvopen & UDF_WRITE_LVINT) {
+               error = udf_writeout_lvint(ump, ump->lvopen);
+               /* if we couldn't write it mark it closed again */
+               if (error) {
+                       ump->logvol_integrity->integrity_type =
+                                               udf_rw32(UDF_INTEGRITY_CLOSED);
+                       return error;
+               }
+       }
+
+       return 0;
+}
+
+
+int
+udf_close_logvol(struct udf_mount *ump, int mntflags)
+{
+       struct vnode *devvp = ump->devvp;
+       struct mmc_op mmc_op;
+       int logvol_integrity;
+       int error = 0, error1 = 0, error2 = 0;
+       int tracknr;
+       int nvats, n, nok;
+
+       /* already/still closed? */
+       logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type);
+       if (logvol_integrity == UDF_INTEGRITY_CLOSED)
+               return 0;
+
+       /* writeout/update lvint or write out VAT */
+       DPRINTF(VOLUMES, ("udf_close_logvol: closing logical volume\n"));
+#ifdef DIAGNOSTIC
+       if (ump->lvclose & UDF_CLOSE_SESSION)
+               KASSERT(ump->lvclose & UDF_WRITE_VAT);
+#endif
+
+       if (ump->lvclose & UDF_WRITE_VAT) {
+               DPRINTF(VOLUMES, ("lvclose & UDF_WRITE_VAT\n"));
+
+               /* write out the VAT data and all its descriptors */
+               DPRINTF(VOLUMES, ("writeout vat_node\n"));
+               udf_writeout_vat(ump);
+               (void) vflushbuf(ump->vat_node->vnode, FSYNC_WAIT);
+
+               (void) VOP_FSYNC(ump->vat_node->vnode,
+                               FSCRED, FSYNC_WAIT, 0, 0);
+
+               if (ump->lvclose & UDF_CLOSE_SESSION) {
+                       DPRINTF(VOLUMES, ("udf_close_logvol: closing session "
+                               "as requested\n"));
+               }
+
+               /* at least two DVD packets and 3 CD-R packets */
+               nvats = 32;
+
+#if notyet
+               /*
+                * TODO calculate the available space and if the disc is
+                * allmost full, write out till end-256-1 with banks, write
+                * AVDP and fill up with VATs, then close session and close
+                * disc.
+                */
+               if (ump->lvclose & UDF_FINALISE_DISC) {
+                       error = udf_write_phys_dscr_sync(ump, NULL,
+                                       UDF_C_FLOAT_DSCR,
+                                       (union dscrptr *) ump->anchors[0],
+                                       0, 0);
+                       if (error)
+                               printf("writeout of anchor failed!\n");
+
+                       /* pad space with VAT ICBs */
+                       nvats = 256;
+               }
+#endif
+
+               /* write out a number of VAT nodes */
+               nok = 0;
+               for (n = 0; n < nvats; n++) {
+                       /* will now only write last FE/EFE */
+                       ump->vat_node->i_flags |= IN_MODIFIED;
+                       error = VOP_FSYNC(ump->vat_node->vnode,
+                                       FSCRED, FSYNC_WAIT, 0, 0);
+                       if (!error)
+                               nok++;
+               }
+               if (nok < 14) {
+                       /* arbitrary; but at least one or two CD frames */
+                       printf("writeout of at least 14 VATs failed\n");
+                       return error;
+               }
+       }
+
+       /* NOTE the disc is in a (minimal) valid state now; no erroring out */
+
+       /* finish closing of session */
+       if (ump->lvclose & UDF_CLOSE_SESSION) {
+               error = udf_validate_session_start(ump);
+               if (error)
+                       return error;
+
+               (void) udf_synchronise_caches(ump);
+
+               /* close all associated tracks */
+               tracknr = ump->discinfo.first_track_last_session;
+               error = 0;
+               while (tracknr <= ump->discinfo.last_track_last_session) {
+                       DPRINTF(VOLUMES, ("\tclosing possible open "
+                               "track %d\n", tracknr));
+                       memset(&mmc_op, 0, sizeof(mmc_op));
+                       mmc_op.operation   = MMC_OP_CLOSETRACK;
+                       mmc_op.mmc_profile = ump->discinfo.mmc_profile;
+                       mmc_op.tracknr     = tracknr;
+                       error = VOP_IOCTL(devvp, MMCOP, &mmc_op,
+                                       FKIOCTL, NOCRED);
+                       if (error)
+                               printf("udf_close_logvol: closing of "
+                                       "track %d failed\n", tracknr);
+                       tracknr ++;
+               }
+               if (!error) {
+                       DPRINTF(VOLUMES, ("closing session\n"));
+                       memset(&mmc_op, 0, sizeof(mmc_op));
+                       mmc_op.operation   = MMC_OP_CLOSESESSION;
+                       mmc_op.mmc_profile = ump->discinfo.mmc_profile;
+                       mmc_op.sessionnr   = ump->discinfo.num_sessions;
+                       error = VOP_IOCTL(devvp, MMCOP, &mmc_op,
+                                       FKIOCTL, NOCRED);
+                       if (error)
+                               printf("udf_close_logvol: closing of session"
+                                               "failed\n");
+               }
+               if (!error)
+                       ump->lvopen |= UDF_OPEN_SESSION;
+               if (error) {
+                       printf("udf_close_logvol: leaving disc as it is\n");
+                       ump->lvclose &= ~UDF_FINALISE_DISC;
+               }
+       }
+
+       if (ump->lvclose & UDF_FINALISE_DISC) {
+               memset(&mmc_op, 0, sizeof(mmc_op));
+               mmc_op.operation   = MMC_OP_FINALISEDISC;
+               mmc_op.mmc_profile = ump->discinfo.mmc_profile;
+               mmc_op.sessionnr   = ump->discinfo.num_sessions;
+               error = VOP_IOCTL(devvp, MMCOP, &mmc_op,
+                               FKIOCTL, NOCRED);
+               if (error)
+                       printf("udf_close_logvol: finalising disc"
+                                       "failed\n");
+       }
+
+       /* write out partition bitmaps if requested */
+       if (ump->lvclose & UDF_WRITE_PART_BITMAPS) {
+               /* sync writeout metadata spacetable if existing */
+               error1 = udf_write_metadata_partition_spacetable(ump, true);
+               if (error1)
+                       printf( "udf_close_logvol: writeout of metadata space "
+                               "bitmap failed\n");
+
+               /* sync writeout partition spacetables */
+               error2 = udf_write_physical_partition_spacetables(ump, true);
+               if (error2)
+                       printf( "udf_close_logvol: writeout of space tables "
+                               "failed\n");
+
+               if (error1 || error2)
+                       return (error1 | error2);
+
+               ump->lvclose &= ~UDF_WRITE_PART_BITMAPS;
+       }
+
+       /* write out metadata partition nodes if requested */
+       if (ump->lvclose & UDF_WRITE_METAPART_NODES) {
+               /* sync writeout metadata descriptor node */
+               error1 = udf_writeout_node(ump->metadata_node, FSYNC_WAIT);
+               if (error1)
+                       printf( "udf_close_logvol: writeout of metadata partition "
+                               "node failed\n");
+
+               /* duplicate metadata partition descriptor if needed */
+               udf_synchronise_metadatamirror_node(ump);
+
+               /* sync writeout metadatamirror descriptor node */
+               error2 = udf_writeout_node(ump->metadatamirror_node, FSYNC_WAIT);
+               if (error2)
+                       printf( "udf_close_logvol: writeout of metadata partition "
+                               "mirror node failed\n");
+
+               if (error1 || error2)
+                       return (error1 | error2);
+
+               ump->lvclose &= ~UDF_WRITE_METAPART_NODES;
+       }
+
+       /* mark it closed */
+       ump->logvol_integrity->integrity_type = udf_rw32(UDF_INTEGRITY_CLOSED);
+
+       /* do we need to write out the logical volume integrity? */
+       if (ump->lvclose & UDF_WRITE_LVINT)
+               error = udf_writeout_lvint(ump, ump->lvopen);
+       if (error) {
+               /* HELP now what? mark it open again for now */
+               ump->logvol_integrity->integrity_type =
+                       udf_rw32(UDF_INTEGRITY_OPEN);
+               return error;
+       }
+
+       (void) udf_synchronise_caches(ump);
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Genfs interfacing
+ *
+ * static const struct genfs_ops udf_genfsops = {
+ *     .gop_size = genfs_size,
+ *             size of transfers
+ *     .gop_alloc = udf_gop_alloc,
+ *             allocate len bytes at offset
+ *     .gop_write = genfs_gop_write,
+ *             putpages interface code
+ *     .gop_markupdate = udf_gop_markupdate,
+ *             set update/modify flags etc.
+ * }
+ */
+
+/*
+ * Genfs interface. These four functions are the only ones defined though not
+ * documented... great....
+ */
+
+/*
+ * Called for allocating an extent of the file either by VOP_WRITE() or by
+ * genfs filling up gaps.
+ */
+static int
+udf_gop_alloc(struct vnode *vp, off_t off,
+    off_t len, int flags, kauth_cred_t cred)
+{
+       struct udf_node *udf_node = VTOI(vp);
+       struct udf_mount *ump = udf_node->ump;
+       uint64_t lb_start, lb_end;
+       uint32_t lb_size, num_lb;
+       int udf_c_type, vpart_num, can_fail;
+       int error;
+
+       DPRINTF(ALLOC, ("udf_gop_alloc called for offset %"PRIu64" for %"PRIu64" bytes, %s\n",
+               off, len, flags? "SYNC":"NONE"));
+
+       /*
+        * request the pages of our vnode and see how many pages will need to
+        * be allocated and reserve that space
+        */
+       lb_size  = udf_rw32(udf_node->ump->logical_vol->lb_size);
+       lb_start = off / lb_size;
+       lb_end   = (off + len + lb_size -1) / lb_size;
+       num_lb   = lb_end - lb_start;
+
+       udf_c_type = udf_get_c_type(udf_node);
+       vpart_num  = udf_get_record_vpart(ump, udf_c_type);
+
+       /* all requests can fail */
+       can_fail   = true;
+
+       /* fid's (directories) can't fail */
+       if (udf_c_type == UDF_C_FIDS)
+               can_fail   = false;
+
+       /* system files can't fail */
+       if (vp->v_vflag & VV_SYSTEM)
+               can_fail = false;
+
+       error = udf_reserve_space(ump, udf_node, udf_c_type,
+               vpart_num, num_lb, can_fail);
+
+       DPRINTF(ALLOC, ("\tlb_start %"PRIu64", lb_end %"PRIu64", num_lb %d\n",
+               lb_start, lb_end, num_lb));
+
+       return error;
+}
+
+
+/*
+ * callback from genfs to update our flags
+ */
+static void
+udf_gop_markupdate(struct vnode *vp, int flags)
+{
+       struct udf_node *udf_node = VTOI(vp);
+       u_long mask = 0;
+
+       if ((flags & GOP_UPDATE_ACCESSED) != 0) {
+               mask = IN_ACCESS;
+       }
+       if ((flags & GOP_UPDATE_MODIFIED) != 0) {
+               if (vp->v_type == VREG) {
+                       mask |= IN_CHANGE | IN_UPDATE;
+               } else {
+                       mask |= IN_MODIFY;
+               }
+       }
+       if (mask) {
+               udf_node->i_flags |= mask;
+       }
+}
+
+
+static const struct genfs_ops udf_genfsops = {
+       .gop_size = genfs_size,
+       .gop_alloc = udf_gop_alloc,
+       .gop_write = genfs_gop_write_rwmap,
+       .gop_markupdate = udf_gop_markupdate,
+};
+
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_write_terminator(struct udf_mount *ump, uint32_t sector)
+{
+       union dscrptr *dscr;
+       int error;
+
+       dscr = malloc(ump->discinfo.sector_size, M_TEMP, M_WAITOK|M_ZERO);
+       udf_inittag(ump, &dscr->tag, TAGID_TERM, sector);
+
+       /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */
+       dscr->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH);
+       (void) udf_validate_tag_and_crc_sums(dscr);
+
+       error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR,
+                       dscr, sector, sector);
+
+       free(dscr, M_TEMP);
+
+       return error;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+/* UDF<->unix converters */
+
+/* --------------------------------------------------------------------- */
+
+static mode_t
+udf_perm_to_unix_mode(uint32_t perm)
+{
+       mode_t mode;
+
+       mode  = ((perm & UDF_FENTRY_PERM_USER_MASK)      );
+       mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK  ) >> 2);
+       mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4);
+
+       return mode;
+}
+
+/* --------------------------------------------------------------------- */
+
+static uint32_t
+unix_mode_to_udf_perm(mode_t mode)
+{
+       uint32_t perm;
+       
+       perm  = ((mode & S_IRWXO)     );
+       perm |= ((mode & S_IRWXG) << 2);
+       perm |= ((mode & S_IRWXU) << 4);
+       perm |= ((mode & S_IWOTH) << 3);
+       perm |= ((mode & S_IWGRP) << 5);
+       perm |= ((mode & S_IWUSR) << 7);
+
+       return perm;
+}
+
+/* --------------------------------------------------------------------- */
+
+static uint32_t
+udf_icb_to_unix_filetype(uint32_t icbftype)
+{
+       switch (icbftype) {
+       case UDF_ICB_FILETYPE_DIRECTORY :
+       case UDF_ICB_FILETYPE_STREAMDIR :
+               return S_IFDIR;
+       case UDF_ICB_FILETYPE_FIFO :
+               return S_IFIFO;
+       case UDF_ICB_FILETYPE_CHARDEVICE :
+               return S_IFCHR;
+       case UDF_ICB_FILETYPE_BLOCKDEVICE :
+               return S_IFBLK;
+       case UDF_ICB_FILETYPE_RANDOMACCESS :
+       case UDF_ICB_FILETYPE_REALTIME :
+               return S_IFREG;
+       case UDF_ICB_FILETYPE_SYMLINK :
+               return S_IFLNK;
+       case UDF_ICB_FILETYPE_SOCKET :
+               return S_IFSOCK;
+       }
+       /* no idea what this is */
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+void
+udf_to_unix_name(char *result, int result_len, char *id, int len,
+       struct charspec *chsp)
+{
+       uint16_t   *raw_name, *unix_name;
+       uint16_t   *inchp, ch;
+       uint8_t    *outchp;
+       const char *osta_id = "OSTA Compressed Unicode";
+       int         ucode_chars, nice_uchars, is_osta_typ0, nout;
+
+       raw_name = malloc(2048 * sizeof(uint16_t), M_UDFTEMP, M_WAITOK);
+       unix_name = raw_name + 1024;                    /* split space in half */
+       assert(sizeof(char) == sizeof(uint8_t));
+       outchp = (uint8_t *) result;
+
+       is_osta_typ0  = (chsp->type == 0);
+       is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0);
+       if (is_osta_typ0) {
+               /* TODO clean up */
+               *raw_name = *unix_name = 0;
+               ucode_chars = udf_UncompressUnicode(len, (uint8_t *) id, raw_name);
+               ucode_chars = MIN(ucode_chars, UnicodeLength((unicode_t *) raw_name));
+               nice_uchars = UDFTransName(unix_name, raw_name, ucode_chars);
+               /* output UTF8 */
+               for (inchp = unix_name; nice_uchars>0; inchp++, nice_uchars--) {
+                       ch = *inchp;
+                       nout = wput_utf8(outchp, result_len, ch);
+                       outchp += nout; result_len -= nout;
+                       if (!ch) break;
+               }
+               *outchp++ = 0;
+       } else {
+               /* assume 8bit char length byte latin-1 */
+               assert(*id == 8);
+               assert(strlen((char *) (id+1)) <= NAME_MAX);
+               strncpy((char *) result, (char *) (id+1), strlen((char *) (id+1)));
+       }
+       free(raw_name, M_UDFTEMP);
+}
+
+/* --------------------------------------------------------------------- */
+
+void
+unix_to_udf_name(char *result, uint8_t *result_len, char const *name, int name_len,
+       struct charspec *chsp)
+{
+       uint16_t   *raw_name;
+       uint16_t   *outchp;
+       const char *inchp;
+       const char *osta_id = "OSTA Compressed Unicode";
+       int         udf_chars, is_osta_typ0, bits;
+       size_t      cnt;
+
+       /* allocate temporary unicode-16 buffer */
+       raw_name = malloc(1024, M_UDFTEMP, M_WAITOK);
+
+       /* convert utf8 to unicode-16 */
+       *raw_name = 0;
+       inchp  = name;
+       outchp = raw_name;
+       bits = 8;
+       for (cnt = name_len, udf_chars = 0; cnt;) {
+               *outchp = wget_utf8(&inchp, &cnt);
+               if (*outchp > 0xff)
+                       bits=16;
+               outchp++;
+               udf_chars++;
+       }
+       /* null terminate just in case */
+       *outchp++ = 0;
+
+       is_osta_typ0  = (chsp->type == 0);
+       is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0);
+       if (is_osta_typ0) {
+               udf_chars = udf_CompressUnicode(udf_chars, bits,
+                               (unicode_t *) raw_name,
+                               (byte *) result);
+       } else {
+               printf("unix to udf name: no CHSP0 ?\n");
+               /* XXX assume 8bit char length byte latin-1 */
+               *result++ = 8; udf_chars = 1;
+               strncpy(result, name + 1, name_len);
+               udf_chars += name_len;
+       }
+       *result_len = udf_chars;
+       free(raw_name, M_UDFTEMP);
+}
+
+/* --------------------------------------------------------------------- */
+
+void
+udf_timestamp_to_timespec(struct udf_mount *ump,
+                         struct timestamp *timestamp,
+                         struct timespec  *timespec)
+{
+       struct clock_ymdhms ymdhms;
+       uint32_t usecs, secs, nsecs;
+       uint16_t tz;
+
+       /* fill in ymdhms structure from timestamp */
+       memset(&ymdhms, 0, sizeof(ymdhms));
+       ymdhms.dt_year = udf_rw16(timestamp->year);
+       ymdhms.dt_mon  = timestamp->month;
+       ymdhms.dt_day  = timestamp->day;
+       ymdhms.dt_wday = 0; /* ? */
+       ymdhms.dt_hour = timestamp->hour;
+       ymdhms.dt_min  = timestamp->minute;
+       ymdhms.dt_sec  = timestamp->second;
+
+       secs = clock_ymdhms_to_secs(&ymdhms);
+       usecs = timestamp->usec +
+               100*timestamp->hund_usec + 10000*timestamp->centisec;
+       nsecs = usecs * 1000;
+
+       /*
+        * Calculate the time zone.  The timezone is 12 bit signed 2's
+        * compliment, so we gotta do some extra magic to handle it right.
+        */
+       tz  = udf_rw16(timestamp->type_tz);
+       tz &= 0x0fff;                   /* only lower 12 bits are significant */
+       if (tz & 0x0800)                /* sign extention */
+               tz |= 0xf000;
+
+       /* TODO check timezone conversion */
+       /* check if we are specified a timezone to convert */
+       if (udf_rw16(timestamp->type_tz) & 0x1000) {
+               if ((int16_t) tz != -2047)
+                       secs -= (int16_t) tz * 60;
+       } else {
+               secs -= ump->mount_args.gmtoff;
+       }
+
+       timespec->tv_sec  = secs;
+       timespec->tv_nsec = nsecs;
+}
+
+
+void
+udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp)
+{
+       struct clock_ymdhms ymdhms;
+       uint32_t husec, usec, csec;
+
+       (void) clock_secs_to_ymdhms(timespec->tv_sec, &ymdhms);
+
+       usec   = timespec->tv_nsec / 1000;
+       husec  =  usec / 100;
+       usec  -= husec * 100;                           /* only 0-99 in usec  */
+       csec   = husec / 100;                           /* only 0-99 in csec  */
+       husec -=  csec * 100;                           /* only 0-99 in husec */
+
+       /* set method 1 for CUT/GMT */
+       timestamp->type_tz      = udf_rw16((1<<12) + 0);
+       timestamp->year         = udf_rw16(ymdhms.dt_year);
+       timestamp->month        = ymdhms.dt_mon;
+       timestamp->day          = ymdhms.dt_day;
+       timestamp->hour         = ymdhms.dt_hour;
+       timestamp->minute       = ymdhms.dt_min;
+       timestamp->second       = ymdhms.dt_sec;
+       timestamp->centisec     = csec;
+       timestamp->hund_usec    = husec;
+       timestamp->usec         = usec;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Attribute and filetypes converters with get/set pairs
+ */
+
+uint32_t
+udf_getaccessmode(struct udf_node *udf_node)
+{
+       struct file_entry     *fe = udf_node->fe;
+       struct extfile_entry *efe = udf_node->efe;
+       uint32_t udf_perm, icbftype;
+       uint32_t mode, ftype;
+       uint16_t icbflags;
+
+       UDF_LOCK_NODE(udf_node, 0);
+       if (fe) {
+               udf_perm = udf_rw32(fe->perm);
+               icbftype = fe->icbtag.file_type;
+               icbflags = udf_rw16(fe->icbtag.flags);
+       } else {
+               assert(udf_node->efe);
+               udf_perm = udf_rw32(efe->perm);
+               icbftype = efe->icbtag.file_type;
+               icbflags = udf_rw16(efe->icbtag.flags);
+       }
+
+       mode  = udf_perm_to_unix_mode(udf_perm);
+       ftype = udf_icb_to_unix_filetype(icbftype);
+
+       /* set suid, sgid, sticky from flags in fe/efe */
+       if (icbflags & UDF_ICB_TAG_FLAGS_SETUID)
+               mode |= S_ISUID;
+       if (icbflags & UDF_ICB_TAG_FLAGS_SETGID)
+               mode |= S_ISGID;
+       if (icbflags & UDF_ICB_TAG_FLAGS_STICKY)
+               mode |= S_ISVTX;
+
+       UDF_UNLOCK_NODE(udf_node, 0);
+
+       return mode | ftype;
+}
+
+
+void
+udf_setaccessmode(struct udf_node *udf_node, mode_t mode)
+{
+       struct file_entry    *fe  = udf_node->fe;
+       struct extfile_entry *efe = udf_node->efe;
+       uint32_t udf_perm;
+       uint16_t icbflags;
+
+       UDF_LOCK_NODE(udf_node, 0);
+       udf_perm = unix_mode_to_udf_perm(mode & ALLPERMS);
+       if (fe) {
+               icbflags = udf_rw16(fe->icbtag.flags);
+       } else {
+               icbflags = udf_rw16(efe->icbtag.flags);
+       }
+
+       icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID;
+       icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID;
+       icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY;
+       if (mode & S_ISUID)
+               icbflags |= UDF_ICB_TAG_FLAGS_SETUID;
+       if (mode & S_ISGID)
+               icbflags |= UDF_ICB_TAG_FLAGS_SETGID;
+       if (mode & S_ISVTX)
+               icbflags |= UDF_ICB_TAG_FLAGS_STICKY;
+
+       if (fe) {
+               fe->perm  = udf_rw32(udf_perm);
+               fe->icbtag.flags  = udf_rw16(icbflags);
+       } else {
+               efe->perm = udf_rw32(udf_perm);
+               efe->icbtag.flags = udf_rw16(icbflags);
+       }
+
+       UDF_UNLOCK_NODE(udf_node, 0);
+}
+
+
+void
+udf_getownership(struct udf_node *udf_node, uid_t *uidp, gid_t *gidp)
+{
+       struct udf_mount     *ump = udf_node->ump;
+       struct file_entry    *fe  = udf_node->fe;
+       struct extfile_entry *efe = udf_node->efe;
+       uid_t uid;
+       gid_t gid;
+
+       UDF_LOCK_NODE(udf_node, 0);
+       if (fe) {
+               uid = (uid_t)udf_rw32(fe->uid);
+               gid = (gid_t)udf_rw32(fe->gid);
+       } else {
+               assert(udf_node->efe);
+               uid = (uid_t)udf_rw32(efe->uid);
+               gid = (gid_t)udf_rw32(efe->gid);
+       }
+       
+       /* do the uid/gid translation game */
+       if (uid == (uid_t) -1)
+               uid = ump->mount_args.anon_uid;
+       if (gid == (gid_t) -1)
+               gid = ump->mount_args.anon_gid;
+
+       *uidp = uid;
+       *gidp = gid;
+
+       UDF_UNLOCK_NODE(udf_node, 0);
+}
+
+
+void
+udf_setownership(struct udf_node *udf_node, uid_t uid, gid_t gid)
+{
+       struct udf_mount     *ump = udf_node->ump;
+       struct file_entry    *fe  = udf_node->fe;
+       struct extfile_entry *efe = udf_node->efe;
+       uid_t nobody_uid;
+       gid_t nobody_gid;
+
+       UDF_LOCK_NODE(udf_node, 0);
+
+       /* do the uid/gid translation game */
+       nobody_uid = ump->mount_args.nobody_uid;
+       nobody_gid = ump->mount_args.nobody_gid;
+       if (uid == nobody_uid)
+               uid = (uid_t) -1;
+       if (gid == nobody_gid)
+               gid = (gid_t) -1;
+
+       if (fe) {
+               fe->uid  = udf_rw32((uint32_t) uid);
+               fe->gid  = udf_rw32((uint32_t) gid);
+       } else {
+               efe->uid = udf_rw32((uint32_t) uid);
+               efe->gid = udf_rw32((uint32_t) gid);
+       }
+
+       UDF_UNLOCK_NODE(udf_node, 0);
+}
+
+
+/* --------------------------------------------------------------------- */
+
+
+int
+udf_dirhash_fill(struct udf_node *dir_node)
+{
+       struct vnode *dvp = dir_node->vnode;
+       struct dirhash *dirh;
+       struct file_entry    *fe  = dir_node->fe;
+       struct extfile_entry *efe = dir_node->efe;
+       struct fileid_desc *fid;
+       struct dirent *dirent;
+       uint64_t file_size, pre_diroffset, diroffset;
+       uint32_t lb_size;
+       int error;
+
+       /* make sure we have a dirhash to work on */
+       dirh = dir_node->dir_hash;
+       KASSERT(dirh);
+       KASSERT(dirh->refcnt > 0);
+
+       if (dirh->flags & DIRH_BROKEN)
+               return EIO;
+       if (dirh->flags & DIRH_COMPLETE)
+               return 0;
+
+       /* make sure we have a clean dirhash to add to */
+       dirhash_purge_entries(dirh);
+
+       /* get directory filesize */
+       if (fe) {
+               file_size = udf_rw64(fe->inf_len);
+       } else {
+               assert(efe);
+               file_size = udf_rw64(efe->inf_len);
+       }
+
+       /* allocate temporary space for fid */
+       lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size);
+       fid = malloc(lb_size, M_UDFTEMP, M_WAITOK);
+
+       /* allocate temporary space for dirent */
+       dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK);
+
+       error = 0;
+       diroffset = 0;
+       while (diroffset < file_size) {
+               /* transfer a new fid/dirent */
+               pre_diroffset = diroffset;
+               error = udf_read_fid_stream(dvp, &diroffset, fid, dirent);
+               if (error) {
+                       /* TODO what to do? continue but not add? */
+                       dirh->flags |= DIRH_BROKEN;
+                       dirhash_purge_entries(dirh);
+                       break;
+               }
+
+               if ((fid->file_char & UDF_FILE_CHAR_DEL)) {
+                       /* register deleted extent for reuse */
+                       dirhash_enter_freed(dirh, pre_diroffset,
+                               udf_fidsize(fid));
+               } else {
+                       /* append to the dirhash */
+                       dirhash_enter(dirh, dirent, pre_diroffset,
+                               udf_fidsize(fid), 0);
+               }
+       }
+       dirh->flags |= DIRH_COMPLETE;
+
+       free(fid, M_UDFTEMP);
+       free(dirent, M_UDFTEMP);
+
+       return error;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Directory read and manipulation functions.
+ *
+ */
+
+int 
+udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen,
+       struct long_ad *icb_loc, int *found)
+{
+       struct udf_node  *dir_node = VTOI(vp);
+       struct dirhash       *dirh;
+       struct dirhash_entry *dirh_ep;
+       struct fileid_desc *fid;
+       struct dirent *dirent;
+       uint64_t diroffset;
+       uint32_t lb_size;
+       int hit, error;
+
+       /* set default return */
+       *found = 0;
+
+       /* get our dirhash and make sure its read in */
+       dirhash_get(&dir_node->dir_hash);
+       error = udf_dirhash_fill(dir_node);
+       if (error) {
+               dirhash_put(dir_node->dir_hash);
+               return error;
+       }
+       dirh = dir_node->dir_hash;
+
+       /* allocate temporary space for fid */
+       lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size);
+       fid     = malloc(lb_size, M_UDFTEMP, M_WAITOK);
+       dirent  = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK);
+
+       DPRINTF(DIRHASH, ("dirhash_lookup looking for `%*.*s`\n",
+               namelen, namelen, name));
+
+       /* search our dirhash hits */
+       memset(icb_loc, 0, sizeof(*icb_loc));
+       dirh_ep = NULL;
+       for (;;) {
+               hit = dirhash_lookup(dirh, name, namelen, &dirh_ep);
+               /* if no hit, abort the search */
+               if (!hit)
+                       break;
+
+               /* check this hit */
+               diroffset = dirh_ep->offset;
+
+               /* transfer a new fid/dirent */
+               error = udf_read_fid_stream(vp, &diroffset, fid, dirent);
+               if (error)
+                       break;
+
+               DPRINTF(DIRHASH, ("dirhash_lookup\tchecking `%*.*s`\n",
+                       dirent->d_namlen, dirent->d_namlen, dirent->d_name));
+
+               /* see if its our entry */
+#ifdef DIAGNOSTIC
+               if (dirent->d_namlen != namelen) {
+                       printf("WARNING: dirhash_lookup() returned wrong "
+                               "d_namelen: %d and ought to be %d\n",
+                               dirent->d_namlen, namelen);
+                       printf("\tlooked for `%s' and got `%s'\n",
+                               name, dirent->d_name);
+               }
+#endif
+               if (strncmp(dirent->d_name, name, namelen) == 0) {
+                       *found = 1;
+                       *icb_loc = fid->icb;
+                       break;
+               }
+       }
+       free(fid, M_UDFTEMP);
+       free(dirent, M_UDFTEMP);
+
+       dirhash_put(dir_node->dir_hash);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_create_new_fe(struct udf_mount *ump, struct file_entry *fe, int file_type,
+       struct long_ad *node_icb, struct long_ad *parent_icb,
+       uint64_t parent_unique_id)
+{
+       struct timespec now;
+       struct icb_tag *icb;
+       struct filetimes_extattr_entry *ft_extattr;
+       uint64_t unique_id;
+       uint32_t fidsize, lb_num;
+       uint8_t *bpos;
+       int crclen, attrlen;
+
+       lb_num = udf_rw32(node_icb->loc.lb_num);
+       udf_inittag(ump, &fe->tag, TAGID_FENTRY, lb_num);
+       icb = &fe->icbtag;
+
+       /*
+        * Always use strategy type 4 unless on WORM wich we don't support
+        * (yet). Fill in defaults and set for internal allocation of data.
+        */
+       icb->strat_type      = udf_rw16(4);
+       icb->max_num_entries = udf_rw16(1);
+       icb->file_type       = file_type;       /* 8 bit */
+       icb->flags           = udf_rw16(UDF_ICB_INTERN_ALLOC);
+
+       fe->perm     = udf_rw32(0x7fff);        /* all is allowed   */
+       fe->link_cnt = udf_rw16(0);             /* explicit setting */
+
+       fe->ckpoint  = udf_rw32(1);             /* user supplied file version */
+
+       vfs_timestamp(&now);
+       udf_timespec_to_timestamp(&now, &fe->atime);
+       udf_timespec_to_timestamp(&now, &fe->attrtime);
+       udf_timespec_to_timestamp(&now, &fe->mtime);
+
+       udf_set_regid(&fe->imp_id, IMPL_NAME);
+       udf_add_impl_regid(ump, &fe->imp_id);
+
+       unique_id = udf_advance_uniqueid(ump);
+       fe->unique_id = udf_rw64(unique_id);
+       fe->l_ea = udf_rw32(0);
+
+       /* create extended attribute to record our creation time */
+       attrlen = UDF_FILETIMES_ATTR_SIZE(1);
+       ft_extattr = malloc(attrlen, M_UDFTEMP, M_WAITOK);
+       memset(ft_extattr, 0, attrlen);
+       ft_extattr->hdr.type = udf_rw32(UDF_FILETIMES_ATTR_NO);
+       ft_extattr->hdr.subtype = 1;    /* [4/48.10.5] */
+       ft_extattr->hdr.a_l = udf_rw32(UDF_FILETIMES_ATTR_SIZE(1));
+       ft_extattr->d_l     = udf_rw32(UDF_TIMESTAMP_SIZE); /* one item */
+       ft_extattr->existence = UDF_FILETIMES_FILE_CREATION;
+       udf_timespec_to_timestamp(&now, &ft_extattr->times[0]);
+
+       udf_extattr_insert_internal(ump, (union dscrptr *) fe,
+               (struct extattr_entry *) ft_extattr);
+       free(ft_extattr, M_UDFTEMP);
+
+       /* if its a directory, create '..' */
+       bpos = (uint8_t *) fe->data + udf_rw32(fe->l_ea);
+       fidsize = 0;
+       if (file_type == UDF_ICB_FILETYPE_DIRECTORY) {
+               fidsize = udf_create_parentfid(ump,
+                       (struct fileid_desc *) bpos, parent_icb,
+                       parent_unique_id);
+       }
+
+       /* record fidlength information */
+       fe->inf_len = udf_rw64(fidsize);
+       fe->l_ad    = udf_rw32(fidsize);
+       fe->logblks_rec = udf_rw64(0);          /* intern */
+
+       crclen  = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH;
+       crclen += udf_rw32(fe->l_ea) + fidsize;
+       fe->tag.desc_crc_len = udf_rw16(crclen);
+
+       (void) udf_validate_tag_and_crc_sums((union dscrptr *) fe);
+
+       return fidsize;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_create_new_efe(struct udf_mount *ump, struct extfile_entry *efe,
+       int file_type, struct long_ad *node_icb, struct long_ad *parent_icb,
+       uint64_t parent_unique_id)
+{
+       struct timespec now;
+       struct icb_tag *icb;
+       uint64_t unique_id;
+       uint32_t fidsize, lb_num;
+       uint8_t *bpos;
+       int crclen;
+
+       lb_num = udf_rw32(node_icb->loc.lb_num);
+       udf_inittag(ump, &efe->tag, TAGID_EXTFENTRY, lb_num);
+       icb = &efe->icbtag;
+
+       /*
+        * Always use strategy type 4 unless on WORM wich we don't support
+        * (yet). Fill in defaults and set for internal allocation of data.
+        */
+       icb->strat_type      = udf_rw16(4);
+       icb->max_num_entries = udf_rw16(1);
+       icb->file_type       = file_type;       /* 8 bit */
+       icb->flags           = udf_rw16(UDF_ICB_INTERN_ALLOC);
+
+       efe->perm     = udf_rw32(0x7fff);       /* all is allowed   */
+       efe->link_cnt = udf_rw16(0);            /* explicit setting */
+
+       efe->ckpoint  = udf_rw32(1);            /* user supplied file version */
+
+       vfs_timestamp(&now);
+       udf_timespec_to_timestamp(&now, &efe->ctime);
+       udf_timespec_to_timestamp(&now, &efe->atime);
+       udf_timespec_to_timestamp(&now, &efe->attrtime);
+       udf_timespec_to_timestamp(&now, &efe->mtime);
+
+       udf_set_regid(&efe->imp_id, IMPL_NAME);
+       udf_add_impl_regid(ump, &efe->imp_id);
+
+       unique_id = udf_advance_uniqueid(ump);
+       efe->unique_id = udf_rw64(unique_id);
+       efe->l_ea = udf_rw32(0);
+
+       /* if its a directory, create '..' */
+       bpos = (uint8_t *) efe->data + udf_rw32(efe->l_ea);
+       fidsize = 0;
+       if (file_type == UDF_ICB_FILETYPE_DIRECTORY) {
+               fidsize = udf_create_parentfid(ump,
+                       (struct fileid_desc *) bpos, parent_icb,
+                       parent_unique_id);
+       }
+
+       /* record fidlength information */
+       efe->obj_size = udf_rw64(fidsize);
+       efe->inf_len  = udf_rw64(fidsize);
+       efe->l_ad     = udf_rw32(fidsize);
+       efe->logblks_rec = udf_rw64(0);         /* intern */
+
+       crclen  = sizeof(struct extfile_entry) - 1 - UDF_DESC_TAG_LENGTH;
+       crclen += udf_rw32(efe->l_ea) + fidsize;
+       efe->tag.desc_crc_len = udf_rw16(crclen);
+
+       (void) udf_validate_tag_and_crc_sums((union dscrptr *) efe);
+
+       return fidsize;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node,
+       struct udf_node *udf_node, struct componentname *cnp)
+{
+       struct vnode *dvp = dir_node->vnode;
+       struct dirhash       *dirh;
+       struct dirhash_entry *dirh_ep;
+       struct file_entry    *fe  = dir_node->fe;
+       struct fileid_desc *fid;
+       struct dirent *dirent;
+       uint64_t diroffset;
+       uint32_t lb_size, fidsize;
+       int found, error;
+       char const *name  = cnp->cn_nameptr;
+       int namelen = cnp->cn_namelen;
+       int hit, refcnt;
+
+       /* get our dirhash and make sure its read in */
+       dirhash_get(&dir_node->dir_hash);
+       error = udf_dirhash_fill(dir_node);
+       if (error) {
+               dirhash_put(dir_node->dir_hash);
+               return error;
+       }
+       dirh = dir_node->dir_hash;
+
+       /* get directory filesize */
+       if (!fe) {
+               assert(dir_node->efe);
+       }
+
+       /* allocate temporary space for fid */
+       lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size);
+       fid     = malloc(lb_size, M_UDFTEMP, M_WAITOK);
+       dirent  = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK);
+
+       /* search our dirhash hits */
+       found = 0;
+       dirh_ep = NULL;
+       for (;;) {
+               hit = dirhash_lookup(dirh, name, namelen, &dirh_ep);
+               /* if no hit, abort the search */
+               if (!hit)
+                       break;
+
+               /* check this hit */
+               diroffset = dirh_ep->offset;
+
+               /* transfer a new fid/dirent */
+               error = udf_read_fid_stream(dvp, &diroffset, fid, dirent);
+               if (error)
+                       break;
+
+               /* see if its our entry */
+               KASSERT(dirent->d_namlen == namelen);
+               if (strncmp(dirent->d_name, name, namelen) == 0) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found)
+               error = ENOENT;
+       if (error)
+               goto error_out;
+
+       /* mark deleted */
+       fid->file_char |= UDF_FILE_CHAR_DEL;
+#ifdef UDF_COMPLETE_DELETE
+       memset(&fid->icb, 0, sizeof(fid->icb));
+#endif
+       (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid);
+
+       /* get size of fid and compensate for the read_fid_stream advance */
+       fidsize = udf_fidsize(fid);
+       diroffset -= fidsize;
+
+       /* write out */
+       error = vn_rdwr(UIO_WRITE, dir_node->vnode,
+                       fid, fidsize, diroffset, 
+                       UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED,
+                       FSCRED, NULL, NULL);
+       if (error)
+               goto error_out;
+
+       /* get reference count of attached node */
+       if (udf_node->fe) {
+               refcnt = udf_rw16(udf_node->fe->link_cnt);
+       } else {
+               KASSERT(udf_node->efe);
+               refcnt = udf_rw16(udf_node->efe->link_cnt);
+       }
+#ifdef UDF_COMPLETE_DELETE
+       /* substract reference counter in attached node */
+       refcnt -= 1;
+       if (udf_node->fe) {
+               udf_node->fe->link_cnt = udf_rw16(refcnt);
+       } else {
+               udf_node->efe->link_cnt = udf_rw16(refcnt);
+       }
+
+       /* prevent writeout when refcnt == 0 */
+       if (refcnt == 0)
+               udf_node->i_flags |= IN_DELETED;
+
+       if (fid->file_char & UDF_FILE_CHAR_DIR) {
+               int drefcnt;
+
+               /* substract reference counter in directory node */
+               /* note subtract 2 (?) for its was also backreferenced */
+               if (dir_node->fe) {
+                       drefcnt  = udf_rw16(dir_node->fe->link_cnt);
+                       drefcnt -= 1;
+                       dir_node->fe->link_cnt = udf_rw16(drefcnt);
+               } else {
+                       KASSERT(dir_node->efe);
+                       drefcnt  = udf_rw16(dir_node->efe->link_cnt);
+                       drefcnt -= 1;
+                       dir_node->efe->link_cnt = udf_rw16(drefcnt);
+               }
+       }
+
+       udf_node->i_flags |= IN_MODIFIED;
+       dir_node->i_flags |= IN_MODIFIED;
+#endif
+       /* if it is/was a hardlink adjust the file count */
+       if (refcnt > 0)
+               udf_adjust_filecount(udf_node, -1);
+
+       /* remove from the dirhash */
+       dirhash_remove(dirh, dirent, diroffset,
+               udf_fidsize(fid));
+
+error_out:
+       free(fid, M_UDFTEMP);
+       free(dirent, M_UDFTEMP);
+
+       dirhash_put(dir_node->dir_hash);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_dir_update_rootentry(struct udf_mount *ump, struct udf_node *dir_node,
+       struct udf_node *new_parent_node)
+{
+       struct vnode *dvp = dir_node->vnode;
+       struct dirhash       *dirh;
+       struct dirhash_entry *dirh_ep;
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       struct fileid_desc *fid;
+       struct dirent *dirent;
+       uint64_t diroffset;
+       uint64_t new_parent_unique_id;
+       uint32_t lb_size, fidsize;
+       int found, error;
+       char const *name  = "..";
+       int namelen = 2;
+       int hit;
+
+       /* get our dirhash and make sure its read in */
+       dirhash_get(&dir_node->dir_hash);
+       error = udf_dirhash_fill(dir_node);
+       if (error) {
+               dirhash_put(dir_node->dir_hash);
+               return error;
+       }
+       dirh = dir_node->dir_hash;
+
+       /* get new parent's unique ID */
+       fe  = new_parent_node->fe;
+       efe = new_parent_node->efe;
+       if (fe) {
+               new_parent_unique_id = udf_rw64(fe->unique_id);
+       } else {
+               assert(efe);
+               new_parent_unique_id = udf_rw64(efe->unique_id);
+       }
+
+       /* get directory filesize */
+       fe  = dir_node->fe;
+       efe = dir_node->efe;
+       if (!fe) {
+               assert(efe);
+       }
+
+       /* allocate temporary space for fid */
+       lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size);
+       fid     = malloc(lb_size, M_UDFTEMP, M_WAITOK);
+       dirent  = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK);
+
+       /*
+        * NOTE the standard does not dictate the FID entry '..' should be
+        * first, though in practice it will most likely be.
+        */
+
+       /* search our dirhash hits */
+       found = 0;
+       dirh_ep = NULL;
+       for (;;) {
+               hit = dirhash_lookup(dirh, name, namelen, &dirh_ep);
+               /* if no hit, abort the search */
+               if (!hit)
+                       break;
+
+               /* check this hit */
+               diroffset = dirh_ep->offset;
+
+               /* transfer a new fid/dirent */
+               error = udf_read_fid_stream(dvp, &diroffset, fid, dirent);
+               if (error)
+                       break;
+
+               /* see if its our entry */
+               KASSERT(dirent->d_namlen == namelen);
+               if (strncmp(dirent->d_name, name, namelen) == 0) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found)
+               error = ENOENT;
+       if (error)
+               goto error_out;
+
+       /* update our ICB to the new parent, hit of lower 32 bits of uniqueid */
+       fid->icb = new_parent_node->write_loc;
+       fid->icb.longad_uniqueid = udf_rw32(new_parent_unique_id);
+
+       (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid);
+
+       /* get size of fid and compensate for the read_fid_stream advance */
+       fidsize = udf_fidsize(fid);
+       diroffset -= fidsize;
+
+       /* write out */
+       error = vn_rdwr(UIO_WRITE, dir_node->vnode,
+                       fid, fidsize, diroffset, 
+                       UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED,
+                       FSCRED, NULL, NULL);
+
+       /* nothing to be done in the dirhash */
+
+error_out:
+       free(fid, M_UDFTEMP);
+       free(dirent, M_UDFTEMP);
+
+       dirhash_put(dir_node->dir_hash);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * We are not allowed to split the fid tag itself over an logical block so
+ * check the space remaining in the logical block.
+ *
+ * We try to select the smallest candidate for recycling or when none is
+ * found, append a new one at the end of the directory.
+ */
+
+int
+udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node,
+       struct udf_node *udf_node, struct vattr *vap, struct componentname *cnp)
+{
+       struct vnode *dvp = dir_node->vnode;
+       struct dirhash       *dirh;
+       struct dirhash_entry *dirh_ep;
+       struct fileid_desc   *fid;
+       struct icb_tag       *icbtag;
+       struct charspec osta_charspec;
+       struct dirent   dirent;
+       uint64_t unique_id, dir_size;
+       uint64_t fid_pos, end_fid_pos, chosen_fid_pos;
+       uint32_t chosen_size, chosen_size_diff;
+       int lb_size, lb_rest, fidsize, this_fidsize, size_diff;
+       int file_char, refcnt, icbflags, addr_type, hit, error;
+
+       /* get our dirhash and make sure its read in */
+       dirhash_get(&dir_node->dir_hash);
+       error = udf_dirhash_fill(dir_node);
+       if (error) {
+               dirhash_put(dir_node->dir_hash);
+               return error;
+       }
+       dirh = dir_node->dir_hash;
+
+       /* get info */
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       udf_osta_charset(&osta_charspec);
+
+       if (dir_node->fe) {
+               dir_size = udf_rw64(dir_node->fe->inf_len);
+               icbtag   = &dir_node->fe->icbtag;
+       } else {
+               dir_size = udf_rw64(dir_node->efe->inf_len);
+               icbtag   = &dir_node->efe->icbtag;
+       }
+
+       icbflags   = udf_rw16(icbtag->flags);
+       addr_type  = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       if (udf_node->fe) {
+               unique_id = udf_rw64(udf_node->fe->unique_id);
+               refcnt    = udf_rw16(udf_node->fe->link_cnt);
+       } else {
+               unique_id = udf_rw64(udf_node->efe->unique_id);
+               refcnt    = udf_rw16(udf_node->efe->link_cnt);
+       }
+
+       if (refcnt > 0) {
+               unique_id = udf_advance_uniqueid(ump);
+               udf_adjust_filecount(udf_node, 1);
+       }
+
+       /* determine file characteristics */
+       file_char = 0;  /* visible non deleted file and not stream metadata */
+       if (vap->va_type == VDIR)
+               file_char = UDF_FILE_CHAR_DIR;
+
+       /* malloc scrap buffer */
+       fid = malloc(lb_size, M_TEMP, M_WAITOK|M_ZERO);
+
+       /* calculate _minimum_ fid size */
+       unix_to_udf_name((char *) fid->data, &fid->l_fi,
+               cnp->cn_nameptr, cnp->cn_namelen, &osta_charspec);
+       fidsize = UDF_FID_SIZE + fid->l_fi;
+       fidsize = (fidsize + 3) & ~3;           /* multiple of 4 */
+
+       /* find position that will fit the FID */
+       chosen_fid_pos   = dir_size;
+       chosen_size      = 0;
+       chosen_size_diff = UINT_MAX;
+
+       /* shut up gcc */
+       dirent.d_namlen = 0;
+
+       /* search our dirhash hits */
+       error = 0;
+       dirh_ep = NULL;
+       for (;;) {
+               hit = dirhash_lookup_freed(dirh, fidsize, &dirh_ep);
+               /* if no hit, abort the search */
+               if (!hit)
+                       break;
+
+               /* check this hit for size */
+               this_fidsize = dirh_ep->entry_size;
+
+               /* check this hit */
+               fid_pos     = dirh_ep->offset;
+               end_fid_pos = fid_pos + this_fidsize;
+               size_diff   = this_fidsize - fidsize;
+               lb_rest = lb_size - (end_fid_pos % lb_size);
+
+#ifndef UDF_COMPLETE_DELETE
+               /* transfer a new fid/dirent */
+               error = udf_read_fid_stream(vp, &fid_pos, fid, dirent);
+               if (error)
+                       goto error_out;
+
+               /* only reuse entries that are wiped */
+               /* check if the len + loc are marked zero */
+               if (udf_rw32(fid->icb.len) != 0)
+                       continue;
+               if (udf_rw32(fid->icb.loc.lb_num) != 0)
+                       continue;
+               if (udf_rw16(fid->icb.loc.part_num) != 0)
+                       continue;
+#endif /* UDF_COMPLETE_DELETE */
+
+               /* select if not splitting the tag and its smaller */
+               if ((size_diff >= 0)  &&
+                       (size_diff < chosen_size_diff) &&
+                       (lb_rest >= sizeof(struct desc_tag)))
+               {
+                       /* UDF 2.3.4.2+3 specifies rules for iu size */
+                       if ((size_diff == 0) || (size_diff >= 32)) {
+                               chosen_fid_pos   = fid_pos;
+                               chosen_size      = this_fidsize;
+                               chosen_size_diff = size_diff;
+                       }
+               }
+       }
+
+
+       /* extend directory if no other candidate found */
+       if (chosen_size == 0) {
+               chosen_fid_pos   = dir_size;
+               chosen_size      = fidsize;
+               chosen_size_diff = 0;
+
+               /* special case UDF 2.00+ 2.3.4.4, no splitting up fid tag */
+               if (addr_type == UDF_ICB_INTERN_ALLOC) {
+                       /* pre-grow directory to see if we're to switch */
+                       udf_grow_node(dir_node, dir_size + chosen_size);
+
+                       icbflags   = udf_rw16(icbtag->flags);
+                       addr_type  = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+               }
+
+               /* make sure the next fid desc_tag won't be splitted */
+               if (addr_type != UDF_ICB_INTERN_ALLOC) {
+                       end_fid_pos = chosen_fid_pos + chosen_size;
+                       lb_rest = lb_size - (end_fid_pos % lb_size);
+
+                       /* pad with implementation use regid if needed */
+                       if (lb_rest < sizeof(struct desc_tag))
+                               chosen_size += 32;
+               }
+       }
+       chosen_size_diff = chosen_size - fidsize;
+
+       /* populate the FID */
+       memset(fid, 0, lb_size);
+       udf_inittag(ump, &fid->tag, TAGID_FID, 0);
+       fid->file_version_num    = udf_rw16(1); /* UDF 2.3.4.1 */
+       fid->file_char           = file_char;
+       fid->icb                 = udf_node->loc;
+       fid->icb.longad_uniqueid = udf_rw32((uint32_t) unique_id);
+       fid->l_iu                = udf_rw16(0);
+
+       if (chosen_size > fidsize) {
+               /* insert implementation-use regid to space it correctly */
+               fid->l_iu = udf_rw16(chosen_size_diff);
+
+               /* set implementation use */
+               udf_set_regid((struct regid *) fid->data, IMPL_NAME);
+               udf_add_impl_regid(ump, (struct regid *) fid->data);
+       }
+
+       /* fill in name */
+       unix_to_udf_name((char *) fid->data + udf_rw16(fid->l_iu),
+               &fid->l_fi, cnp->cn_nameptr, cnp->cn_namelen, &osta_charspec);
+
+       fid->tag.desc_crc_len = udf_rw16(chosen_size - UDF_DESC_TAG_LENGTH);
+       (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid);
+
+       /* writeout FID/update parent directory */
+       error = vn_rdwr(UIO_WRITE, dvp,
+                       fid, chosen_size, chosen_fid_pos, 
+                       UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED,
+                       FSCRED, NULL, NULL);
+
+       if (error)
+               goto error_out;
+
+       /* add reference counter in attached node */
+       if (udf_node->fe) {
+               refcnt = udf_rw16(udf_node->fe->link_cnt);
+               udf_node->fe->link_cnt = udf_rw16(refcnt+1);
+       } else {
+               KASSERT(udf_node->efe);
+               refcnt = udf_rw16(udf_node->efe->link_cnt);
+               udf_node->efe->link_cnt = udf_rw16(refcnt+1);
+       }
+
+       /* mark not deleted if it was... just in case, but do warn */
+       if (udf_node->i_flags & IN_DELETED) {
+               printf("udf: warning, marking a file undeleted\n");
+               udf_node->i_flags &= ~IN_DELETED;
+       }
+
+       if (file_char & UDF_FILE_CHAR_DIR) {
+               /* add reference counter in directory node for '..' */
+               if (dir_node->fe) {
+                       refcnt = udf_rw16(dir_node->fe->link_cnt);
+                       refcnt++;
+                       dir_node->fe->link_cnt = udf_rw16(refcnt);
+               } else {
+                       KASSERT(dir_node->efe);
+                       refcnt = udf_rw16(dir_node->efe->link_cnt);
+                       refcnt++;
+                       dir_node->efe->link_cnt = udf_rw16(refcnt);
+               }
+       }
+
+       /* append to the dirhash */
+       /* NOTE do not use dirent anymore or it won't match later! */
+       udf_to_unix_name(dirent.d_name, NAME_MAX,
+               (char *) fid->data + udf_rw16(fid->l_iu), fid->l_fi, &osta_charspec);
+       dirent.d_namlen = strlen(dirent.d_name);
+       dirhash_enter(dirh, &dirent, chosen_fid_pos,
+               udf_fidsize(fid), 1);
+
+       /* note updates */
+       udf_node->i_flags |= IN_CHANGE | IN_MODIFY; /* | IN_CREATE? */
+       /* VN_KNOTE(udf_node,  ...) */
+       udf_update(udf_node->vnode, NULL, NULL, NULL, 0);
+
+error_out:
+       free(fid, M_TEMP);
+
+       dirhash_put(dir_node->dir_hash);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Each node can have an attached streamdir node though not recursively. These
+ * are otherwise known as named substreams/named extended attributes that have
+ * no size limitations.
+ *
+ * `Normal' extended attributes are indicated with a number and are recorded
+ * in either the fe/efe descriptor itself for small descriptors or recorded in
+ * the attached extended attribute file. Since these spaces can get
+ * fragmented, care ought to be taken.
+ *
+ * Since the size of the space reserved for allocation descriptors is limited,
+ * there is a mechanim provided for extending this space; this is done by a
+ * special extent to allow schrinking of the allocations without breaking the
+ * linkage to the allocation extent descriptor.
+ */
+
+int
+udf_get_node(struct udf_mount *ump, struct long_ad *node_icb_loc,
+            struct udf_node **udf_noderes)
+{
+       union dscrptr   *dscr;
+       struct udf_node *udf_node;
+       struct vnode    *nvp;
+       struct long_ad   icb_loc, last_fe_icb_loc;
+       uint64_t file_size;
+       uint32_t lb_size, sector, dummy;
+       int udf_file_type, dscr_type, strat, strat4096, needs_indirect;
+       int slot, eof, error;
+
+       DPRINTF(NODE, ("udf_get_node called\n"));
+       *udf_noderes = udf_node = NULL;
+
+       /* lock to disallow simultanious creation of same udf_node */
+       mutex_enter(&ump->get_node_lock);
+
+       DPRINTF(NODE, ("\tlookup in hash table\n"));
+       /* lookup in hash table */
+       assert(ump);
+       assert(node_icb_loc);
+       udf_node = udf_node_lookup(ump, node_icb_loc);
+       if (udf_node) {
+               DPRINTF(NODE, ("\tgot it from the hash!\n"));
+               /* vnode is returned locked */
+               *udf_noderes = udf_node;
+               mutex_exit(&ump->get_node_lock);
+               return 0;
+       }
+
+       /* garbage check: translate udf_node_icb_loc to sectornr */
+       error = udf_translate_vtop(ump, node_icb_loc, &sector, &dummy);
+       if (error) {
+               DPRINTF(NODE, ("\tcan't translate icb address!\n"));
+               /* no use, this will fail anyway */
+               mutex_exit(&ump->get_node_lock);
+               return EINVAL;
+       }
+
+       /* build udf_node (do initialise!) */
+       udf_node = pool_get(&udf_node_pool, PR_WAITOK);
+       memset(udf_node, 0, sizeof(struct udf_node));
+
+       DPRINTF(NODE, ("\tget new vnode\n"));
+       /* give it a vnode */
+       error = getnewvnode(VT_UDF, ump->vfs_mountp, udf_vnodeop_p, NULL, &nvp);
+       if (error) {
+               pool_put(&udf_node_pool, udf_node);
+               mutex_exit(&ump->get_node_lock);
+               return error;
+       }
+
+       /* always return locked vnode */
+       if ((error = vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY))) {
+               /* recycle vnode and unlock; simultanious will fail too */
+               ungetnewvnode(nvp);
+               mutex_exit(&ump->get_node_lock);
+               return error;
+       }
+
+       /* initialise crosslinks, note location of fe/efe for hashing */
+       udf_node->ump    =  ump;
+       udf_node->vnode  =  nvp;
+       nvp->v_data      =  udf_node;
+       udf_node->loc    = *node_icb_loc;
+       udf_node->lockf  =  0;
+       mutex_init(&udf_node->node_mutex, MUTEX_DEFAULT, IPL_NONE);
+       cv_init(&udf_node->node_lock, "udf_nlk");
+       genfs_node_init(nvp, &udf_genfsops);    /* inititise genfs */
+       udf_node->outstanding_bufs = 0;
+       udf_node->outstanding_nodedscr = 0;
+       udf_node->uncommitted_lbs = 0;
+
+       /* check if we're fetching the root */
+       if (ump->fileset_desc)
+               if (memcmp(&udf_node->loc, &ump->fileset_desc->rootdir_icb,
+                   sizeof(struct long_ad)) == 0)
+                       nvp->v_vflag |= VV_ROOT;
+
+       /* insert into the hash lookup */
+       udf_register_node(udf_node);
+
+       /* safe to unlock, the entry is in the hash table, vnode is locked */
+       mutex_exit(&ump->get_node_lock);
+
+       icb_loc = *node_icb_loc;
+       needs_indirect = 0;
+       strat4096 = 0;
+       udf_file_type = UDF_ICB_FILETYPE_UNKNOWN;
+       file_size = 0;
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+
+       DPRINTF(NODE, ("\tstart reading descriptors\n"));
+       do {
+               /* try to read in fe/efe */
+               error = udf_read_logvol_dscr(ump, &icb_loc, &dscr);
+
+               /* blank sector marks end of sequence, check this */
+               if ((dscr == NULL) &&  (!strat4096))
+                       error = ENOENT;
+
+               /* break if read error or blank sector */
+               if (error || (dscr == NULL))
+                       break;
+
+               /* process descriptor based on the descriptor type */
+               dscr_type = udf_rw16(dscr->tag.id);
+               DPRINTF(NODE, ("\tread descriptor %d\n", dscr_type));
+
+               /* if dealing with an indirect entry, follow the link */
+               if (dscr_type == TAGID_INDIRECTENTRY) {
+                       needs_indirect = 0;
+                       udf_free_logvol_dscr(ump, &icb_loc, dscr);
+                       icb_loc = dscr->inde.indirect_icb;
+                       continue;
+               }
+
+               /* only file entries and extended file entries allowed here */
+               if ((dscr_type != TAGID_FENTRY) &&
+                   (dscr_type != TAGID_EXTFENTRY)) {
+                       udf_free_logvol_dscr(ump, &icb_loc, dscr);
+                       error = ENOENT;
+                       break;
+               }
+
+               KASSERT(udf_tagsize(dscr, lb_size) == lb_size);
+
+               /* choose this one */
+               last_fe_icb_loc = icb_loc;
+               
+               /* record and process/update (ext)fentry */
+               if (dscr_type == TAGID_FENTRY) {
+                       if (udf_node->fe)
+                               udf_free_logvol_dscr(ump, &last_fe_icb_loc,
+                                       udf_node->fe);
+                       udf_node->fe  = &dscr->fe;
+                       strat = udf_rw16(udf_node->fe->icbtag.strat_type);
+                       udf_file_type = udf_node->fe->icbtag.file_type;
+                       file_size = udf_rw64(udf_node->fe->inf_len);
+               } else {
+                       if (udf_node->efe)
+                               udf_free_logvol_dscr(ump, &last_fe_icb_loc,
+                                       udf_node->efe);
+                       udf_node->efe = &dscr->efe;
+                       strat = udf_rw16(udf_node->efe->icbtag.strat_type);
+                       udf_file_type = udf_node->efe->icbtag.file_type;
+                       file_size = udf_rw64(udf_node->efe->inf_len);
+               }
+
+               /* check recording strategy (structure) */
+
+               /*
+                * Strategy 4096 is a daisy linked chain terminating with an
+                * unrecorded sector or a TERM descriptor. The next
+                * descriptor is to be found in the sector that follows the
+                * current sector.
+                */
+               if (strat == 4096) {
+                       strat4096 = 1;
+                       needs_indirect = 1;
+
+                       icb_loc.loc.lb_num = udf_rw32(icb_loc.loc.lb_num) + 1;
+               }
+
+               /*
+                * Strategy 4 is the normal strategy and terminates, but if
+                * we're in strategy 4096, we can't have strategy 4 mixed in
+                */
+
+               if (strat == 4) {
+                       if (strat4096) {
+                               error = EINVAL;
+                               break;
+                       }
+                       break;          /* done */
+               }
+       } while (!error);
+
+       /* first round of cleanup code */
+       if (error) {
+               DPRINTF(NODE, ("\tnode fe/efe failed!\n"));
+               /* recycle udf_node */
+               udf_dispose_node(udf_node);
+
+               VOP_UNLOCK(nvp);
+               nvp->v_data = NULL;
+               ungetnewvnode(nvp);
+
+               return EINVAL;          /* error code ok? */
+       }
+       DPRINTF(NODE, ("\tnode fe/efe read in fine\n"));
+
+       /* assert no references to dscr anymore beyong this point */
+       assert((udf_node->fe) || (udf_node->efe));
+       dscr = NULL;
+
+       /*
+        * Remember where to record an updated version of the descriptor. If
+        * there is a sequence of indirect entries, icb_loc will have been
+        * updated. Its the write disipline to allocate new space and to make
+        * sure the chain is maintained.
+        *
+        * `needs_indirect' flags if the next location is to be filled with
+        * with an indirect entry.
+        */
+       udf_node->write_loc = icb_loc;
+       udf_node->needs_indirect = needs_indirect;
+
+       /*
+        * Go trough all allocations extents of this descriptor and when
+        * encountering a redirect read in the allocation extension. These are
+        * daisy-chained.
+        */
+       UDF_LOCK_NODE(udf_node, 0);
+       udf_node->num_extensions = 0;
+
+       error   = 0;
+       slot    = 0;
+       for (;;) {
+               udf_get_adslot(udf_node, slot, &icb_loc, &eof);
+               DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, len = %d, "
+                       "lb_num = %d, part = %d\n", slot, eof,
+                       UDF_EXT_FLAGS(udf_rw32(icb_loc.len)),
+                       UDF_EXT_LEN(udf_rw32(icb_loc.len)),
+                       udf_rw32(icb_loc.loc.lb_num),
+                       udf_rw16(icb_loc.loc.part_num)));
+               if (eof)
+                       break;
+               slot++;
+
+               if (UDF_EXT_FLAGS(udf_rw32(icb_loc.len)) != UDF_EXT_REDIRECT)
+                       continue;
+
+               DPRINTF(NODE, ("\tgot redirect extent\n"));
+               if (udf_node->num_extensions >= UDF_MAX_ALLOC_EXTENTS) {
+                       DPRINTF(ALLOC, ("udf_get_node: implementation limit, "
+                                       "too many allocation extensions on "
+                                       "udf_node\n"));
+                       error = EINVAL;
+                       break;
+               }
+
+               /* length can only be *one* lb : UDF 2.50/2.3.7.1 */
+               if (UDF_EXT_LEN(udf_rw32(icb_loc.len)) != lb_size) {
+                       DPRINTF(ALLOC, ("udf_get_node: bad allocation "
+                                       "extension size in udf_node\n"));
+                       error = EINVAL;
+                       break;
+               }
+
+               DPRINTF(NODE, ("read allocation extent at lb_num %d\n",
+                       UDF_EXT_LEN(udf_rw32(icb_loc.loc.lb_num))));
+               /* load in allocation extent */
+               error = udf_read_logvol_dscr(ump, &icb_loc, &dscr);
+               if (error || (dscr == NULL))
+                       break;
+
+               /* process read-in descriptor */
+               dscr_type = udf_rw16(dscr->tag.id);
+
+               if (dscr_type != TAGID_ALLOCEXTENT) {
+                       udf_free_logvol_dscr(ump, &icb_loc, dscr);
+                       error = ENOENT;
+                       break;
+               }
+
+               DPRINTF(NODE, ("\trecording redirect extent\n"));
+               udf_node->ext[udf_node->num_extensions] = &dscr->aee;
+               udf_node->ext_loc[udf_node->num_extensions] = icb_loc;
+
+               udf_node->num_extensions++;
+
+       } /* while */
+       UDF_UNLOCK_NODE(udf_node, 0);
+
+       /* second round of cleanup code */
+       if (error) {
+               /* recycle udf_node */
+               udf_dispose_node(udf_node);
+
+               VOP_UNLOCK(nvp);
+               nvp->v_data = NULL;
+               ungetnewvnode(nvp);
+
+               return EINVAL;          /* error code ok? */
+       }
+
+       DPRINTF(NODE, ("\tnode read in fine\n"));
+
+       /*
+        * Translate UDF filetypes into vnode types.
+        *
+        * Systemfiles like the meta main and mirror files are not treated as
+        * normal files, so we type them as having no type. UDF dictates that
+        * they are not allowed to be visible.
+        */
+
+       switch (udf_file_type) {
+       case UDF_ICB_FILETYPE_DIRECTORY :
+       case UDF_ICB_FILETYPE_STREAMDIR :
+               nvp->v_type = VDIR;
+               break;
+       case UDF_ICB_FILETYPE_BLOCKDEVICE :
+               nvp->v_type = VBLK;
+               break;
+       case UDF_ICB_FILETYPE_CHARDEVICE :
+               nvp->v_type = VCHR;
+               break;
+       case UDF_ICB_FILETYPE_SOCKET :
+               nvp->v_type = VSOCK;
+               break;
+       case UDF_ICB_FILETYPE_FIFO :
+               nvp->v_type = VFIFO;
+               break;
+       case UDF_ICB_FILETYPE_SYMLINK :
+               nvp->v_type = VLNK;
+               break;
+       case UDF_ICB_FILETYPE_VAT :
+       case UDF_ICB_FILETYPE_META_MAIN :
+       case UDF_ICB_FILETYPE_META_MIRROR :
+               nvp->v_type = VNON;
+               break;
+       case UDF_ICB_FILETYPE_RANDOMACCESS :
+       case UDF_ICB_FILETYPE_REALTIME :
+               nvp->v_type = VREG;
+               break;
+       default:
+               /* YIKES, something else */
+               nvp->v_type = VNON;
+       }
+
+       /* TODO specfs, fifofs etc etc. vnops setting */
+
+       /* don't forget to set vnode's v_size */
+       uvm_vnp_setsize(nvp, file_size);
+
+       /* TODO ext attr and streamdir udf_nodes */
+
+       *udf_noderes = udf_node;
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_writeout_node(struct udf_node *udf_node, int waitfor)
+{
+       union dscrptr *dscr;
+       struct long_ad *loc;
+       int extnr, error;
+
+       DPRINTF(NODE, ("udf_writeout_node called\n"));
+
+       KASSERT(udf_node->outstanding_bufs == 0);
+       KASSERT(udf_node->outstanding_nodedscr == 0);
+
+       KASSERT(LIST_EMPTY(&udf_node->vnode->v_dirtyblkhd));
+
+       if (udf_node->i_flags & IN_DELETED) {
+               DPRINTF(NODE, ("\tnode deleted; not writing out\n"));
+               udf_cleanup_reservation(udf_node);
+               return 0;
+       }
+
+       /* lock node; unlocked in callback */
+       UDF_LOCK_NODE(udf_node, 0);
+
+       /* remove pending reservations, we're written out */
+       udf_cleanup_reservation(udf_node);
+
+       /* at least one descriptor writeout */
+       udf_node->outstanding_nodedscr = 1;
+
+       /* we're going to write out the descriptor so clear the flags */
+       udf_node->i_flags &= ~(IN_MODIFIED | IN_ACCESSED);
+
+       /* if we were rebuild, write out the allocation extents */
+       if (udf_node->i_flags & IN_NODE_REBUILD) {
+               /* mark outstanding node descriptors and issue them */
+               udf_node->outstanding_nodedscr += udf_node->num_extensions;
+               for (extnr = 0; extnr < udf_node->num_extensions; extnr++) {
+                       loc = &udf_node->ext_loc[extnr];
+                       dscr = (union dscrptr *) udf_node->ext[extnr];
+                       error = udf_write_logvol_dscr(udf_node, dscr, loc, 0);
+                       if (error)
+                               return error;
+               }
+               /* mark allocation extents written out */
+               udf_node->i_flags &= ~(IN_NODE_REBUILD);
+       }
+
+       if (udf_node->fe) {
+               KASSERT(udf_node->efe == NULL);
+               dscr = (union dscrptr *) udf_node->fe;
+       } else {
+               KASSERT(udf_node->efe);
+               KASSERT(udf_node->fe == NULL);
+               dscr = (union dscrptr *) udf_node->efe;
+       }
+       KASSERT(dscr);
+
+       loc = &udf_node->write_loc;
+       error = udf_write_logvol_dscr(udf_node, dscr, loc, waitfor);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_dispose_node(struct udf_node *udf_node)
+{
+       struct vnode *vp;
+       int extnr;
+
+       DPRINTF(NODE, ("udf_dispose_node called on node %p\n", udf_node));
+       if (!udf_node) {
+               DPRINTF(NODE, ("UDF: Dispose node on node NULL, ignoring\n"));
+               return 0;
+       }
+
+       vp  = udf_node->vnode;
+#ifdef DIAGNOSTIC
+       if (vp->v_numoutput)
+               panic("disposing UDF node with pending I/O's, udf_node = %p, "
+                               "v_numoutput = %d", udf_node, vp->v_numoutput);
+#endif
+
+       udf_cleanup_reservation(udf_node);
+
+       /* TODO extended attributes and streamdir */
+
+       /* remove dirhash if present */
+       dirhash_purge(&udf_node->dir_hash);
+
+       /* remove from our hash lookup table */
+       udf_deregister_node(udf_node);
+
+       /* destroy our lock */
+       mutex_destroy(&udf_node->node_mutex);
+       cv_destroy(&udf_node->node_lock);
+
+       /* dissociate our udf_node from the vnode */
+       genfs_node_destroy(udf_node->vnode);
+       vp->v_data = NULL;
+
+       /* free associated memory and the node itself */
+       for (extnr = 0; extnr < udf_node->num_extensions; extnr++) {
+               udf_free_logvol_dscr(udf_node->ump, &udf_node->ext_loc[extnr],
+                       udf_node->ext[extnr]);
+               udf_node->ext[extnr] = (void *) 0xdeadcccc;
+       }
+
+       if (udf_node->fe)
+               udf_free_logvol_dscr(udf_node->ump, &udf_node->loc,
+                       udf_node->fe);
+       if (udf_node->efe)
+               udf_free_logvol_dscr(udf_node->ump, &udf_node->loc,
+                       udf_node->efe);
+
+       udf_node->fe  = (void *) 0xdeadaaaa;
+       udf_node->efe = (void *) 0xdeadbbbb;
+       udf_node->ump = (void *) 0xdeadbeef;
+       pool_put(&udf_node_pool, udf_node);
+
+       return 0;
+}
+
+
+
+/*
+ * create a new node using the specified vnodeops, vap and cnp but with the
+ * udf_file_type. This allows special files to be created. Use with care.
+ */
+
+static int
+udf_create_node_raw(struct vnode *dvp, struct vnode **vpp, int udf_file_type,
+       int (**vnodeops)(void *), struct vattr *vap, struct componentname *cnp)
+{
+       union dscrptr *dscr;
+       struct udf_node *dir_node = VTOI(dvp);
+       struct udf_node *udf_node;
+       struct udf_mount *ump = dir_node->ump;
+       struct vnode *nvp;
+       struct long_ad node_icb_loc;
+       uint64_t parent_unique_id;
+       uint64_t lmapping;
+       uint32_t lb_size, lb_num;
+       uint16_t vpart_num;
+       uid_t uid;
+       gid_t gid, parent_gid;
+       int fid_size, error;
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       *vpp = NULL;
+
+       /* allocate vnode */
+       error = getnewvnode(VT_UDF, ump->vfs_mountp, vnodeops, NULL, &nvp);
+       if (error)
+               return error;
+
+       /* lock node */
+       error = vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY);
+       if (error)
+               goto error_out_unget;
+
+       /* reserve space for one logical block */
+       vpart_num = ump->node_part;
+       error = udf_reserve_space(ump, NULL, UDF_C_NODE,
+               vpart_num, 1, /* can_fail */ true);
+       if (error)
+               goto error_out_unlock;
+
+       /* allocate node */
+       error = udf_allocate_space(ump, NULL, UDF_C_NODE,
+                       vpart_num, 1, &lmapping);
+       if (error)
+               goto error_out_unreserve;
+       lb_num = lmapping;
+
+       /* initialise pointer to location */
+       memset(&node_icb_loc, 0, sizeof(struct long_ad));
+       node_icb_loc.len = udf_rw32(lb_size);
+       node_icb_loc.loc.lb_num   = udf_rw32(lb_num);
+       node_icb_loc.loc.part_num = udf_rw16(vpart_num);
+
+       /* build udf_node (do initialise!) */
+       udf_node = pool_get(&udf_node_pool, PR_WAITOK);
+       memset(udf_node, 0, sizeof(struct udf_node));
+
+       /* initialise crosslinks, note location of fe/efe for hashing */
+       /* bugalert: synchronise with udf_get_node() */
+       udf_node->ump       = ump;
+       udf_node->vnode     = nvp;
+       nvp->v_data         = udf_node;
+       udf_node->loc       = node_icb_loc;
+       udf_node->write_loc = node_icb_loc;
+       udf_node->lockf     = 0;
+       mutex_init(&udf_node->node_mutex, MUTEX_DEFAULT, IPL_NONE);
+       cv_init(&udf_node->node_lock, "udf_nlk");
+       udf_node->outstanding_bufs = 0;
+       udf_node->outstanding_nodedscr = 0;
+       udf_node->uncommitted_lbs = 0;
+
+       /* initialise genfs */
+       genfs_node_init(nvp, &udf_genfsops);
+
+       /* insert into the hash lookup */
+       udf_register_node(udf_node);
+
+       /* get parent's unique ID for refering '..' if its a directory */
+       if (dir_node->fe) {
+               parent_unique_id = udf_rw64(dir_node->fe->unique_id);
+               parent_gid       = (gid_t) udf_rw32(dir_node->fe->gid);
+       } else {
+               parent_unique_id = udf_rw64(dir_node->efe->unique_id);
+               parent_gid       = (gid_t) udf_rw32(dir_node->efe->gid);
+       }
+
+       /* get descriptor */
+       udf_create_logvol_dscr(ump, udf_node, &node_icb_loc, &dscr);
+
+       /* choose a fe or an efe for it */
+       if (udf_rw16(ump->logical_vol->tag.descriptor_ver) == 2) {
+               udf_node->fe = &dscr->fe;
+               fid_size = udf_create_new_fe(ump, udf_node->fe,
+                       udf_file_type, &udf_node->loc,
+                       &dir_node->loc, parent_unique_id);
+               /* TODO add extended attribute for creation time */
+       } else {
+               udf_node->efe = &dscr->efe;
+               fid_size = udf_create_new_efe(ump, udf_node->efe,
+                       udf_file_type, &udf_node->loc,
+                       &dir_node->loc, parent_unique_id);
+       }
+       KASSERT(dscr->tag.tag_loc == udf_node->loc.loc.lb_num);
+
+       /* update vnode's size and type */
+       nvp->v_type = vap->va_type;
+       uvm_vnp_setsize(nvp, fid_size);
+
+       /* set access mode */
+       udf_setaccessmode(udf_node, vap->va_mode);
+
+       /* set ownership */
+       uid = kauth_cred_geteuid(cnp->cn_cred);
+       gid = parent_gid;
+       udf_setownership(udf_node, uid, gid);
+
+       error = udf_dir_attach(ump, dir_node, udf_node, vap, cnp);
+       if (error) {
+               /* free disc allocation for node */
+               udf_free_allocated_space(ump, lb_num, vpart_num, 1);
+
+               /* recycle udf_node */
+               udf_dispose_node(udf_node);
+               vput(nvp);
+
+               *vpp = NULL;
+               return error;
+       }
+
+       /* adjust file count */
+       udf_adjust_filecount(udf_node, 1);
+
+       /* return result */
+       *vpp = nvp;
+
+       return 0;
+
+error_out_unreserve:
+       udf_do_unreserve_space(ump, NULL, vpart_num, 1);
+
+error_out_unlock:
+       VOP_UNLOCK(nvp);
+
+error_out_unget:
+       nvp->v_data = NULL;
+       ungetnewvnode(nvp);
+
+       return error;
+}
+
+
+int
+udf_create_node(struct vnode *dvp, struct vnode **vpp, struct vattr *vap,
+       struct componentname *cnp)
+{
+       int (**vnodeops)(void *);
+       int udf_file_type;
+
+       DPRINTF(NODE, ("udf_create_node called\n"));
+
+       /* what type are we creating ? */
+       vnodeops = udf_vnodeop_p;
+       /* start with a default */
+       udf_file_type = UDF_ICB_FILETYPE_RANDOMACCESS;
+
+       *vpp = NULL;
+
+       switch (vap->va_type) {
+       case VREG :
+               udf_file_type = UDF_ICB_FILETYPE_RANDOMACCESS;
+               break;
+       case VDIR :
+               udf_file_type = UDF_ICB_FILETYPE_DIRECTORY;
+               break;
+       case VLNK :
+               udf_file_type = UDF_ICB_FILETYPE_SYMLINK;
+               break;
+       case VBLK :
+               udf_file_type = UDF_ICB_FILETYPE_BLOCKDEVICE;
+               /* specfs */
+               return ENOTSUP;
+               break;
+       case VCHR :
+               udf_file_type = UDF_ICB_FILETYPE_CHARDEVICE;
+               /* specfs */
+               return ENOTSUP;
+               break;
+       case VFIFO :
+               udf_file_type = UDF_ICB_FILETYPE_FIFO;
+               /* specfs */
+               return ENOTSUP;
+               break;
+       case VSOCK :
+               udf_file_type = UDF_ICB_FILETYPE_SOCKET;
+               /* specfs */
+               return ENOTSUP;
+               break;
+       case VNON :
+       case VBAD :
+       default :
+               /* nothing; can we even create these? */
+               return EINVAL;
+       }
+
+       return udf_create_node_raw(dvp, vpp, udf_file_type, vnodeops, vap, cnp);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void
+udf_free_descriptor_space(struct udf_node *udf_node, struct long_ad *loc, void *mem)
+{
+       struct udf_mount *ump = udf_node->ump;
+       uint32_t lb_size, lb_num, len, num_lb;
+       uint16_t vpart_num;
+
+       /* is there really one? */
+       if (mem == NULL)
+               return;
+
+       /* got a descriptor here */
+       len       = UDF_EXT_LEN(udf_rw32(loc->len));
+       lb_num    = udf_rw32(loc->loc.lb_num);
+       vpart_num = udf_rw16(loc->loc.part_num);
+
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       num_lb = (len + lb_size -1) / lb_size;
+
+       udf_free_allocated_space(ump, lb_num, vpart_num, num_lb);
+}
+
+void
+udf_delete_node(struct udf_node *udf_node)
+{
+       void *dscr;
+       struct long_ad *loc;
+       int extnr, lvint, dummy;
+
+       /* paranoia check on integrity; should be open!; we could panic */
+       lvint = udf_rw32(udf_node->ump->logvol_integrity->integrity_type);
+       if (lvint == UDF_INTEGRITY_CLOSED)
+               printf("\tIntegrity was CLOSED!\n");
+
+       /* whatever the node type, change its size to zero */
+       (void) udf_resize_node(udf_node, 0, &dummy);
+
+       /* force it to be `clean'; no use writing it out */
+       udf_node->i_flags &= ~(IN_MODIFIED | IN_ACCESSED | IN_ACCESS |
+               IN_CHANGE | IN_UPDATE | IN_MODIFY);
+
+       /* adjust file count */
+       udf_adjust_filecount(udf_node, -1);
+
+       /*
+        * Free its allocated descriptors; memory will be released when
+        * vop_reclaim() is called.
+        */
+       loc = &udf_node->loc;
+
+       dscr = udf_node->fe;
+       udf_free_descriptor_space(udf_node, loc, dscr);
+       dscr = udf_node->efe;
+       udf_free_descriptor_space(udf_node, loc, dscr);
+
+       for (extnr = 0; extnr < UDF_MAX_ALLOC_EXTENTS; extnr++) {
+               dscr =  udf_node->ext[extnr];
+               loc  = &udf_node->ext_loc[extnr];
+               udf_free_descriptor_space(udf_node, loc, dscr);
+       }
+}
+
+/* --------------------------------------------------------------------- */
+
+/* set new filesize; node but be LOCKED on entry and is locked on exit */
+int
+udf_resize_node(struct udf_node *udf_node, uint64_t new_size, int *extended)
+{
+       struct file_entry    *fe  = udf_node->fe;
+       struct extfile_entry *efe = udf_node->efe;
+       uint64_t file_size;
+       int error;
+
+       if (fe) {
+               file_size  = udf_rw64(fe->inf_len);
+       } else {
+               assert(udf_node->efe);
+               file_size  = udf_rw64(efe->inf_len);
+       }
+
+       DPRINTF(ATTR, ("\tchanging file length from %"PRIu64" to %"PRIu64"\n",
+                       file_size, new_size));
+
+       /* if not changing, we're done */
+       if (file_size == new_size)
+               return 0;
+
+       *extended = (new_size > file_size);
+       if (*extended) {
+               error = udf_grow_node(udf_node, new_size);
+       } else {
+               error = udf_shrink_node(udf_node, new_size);
+       }
+
+       return error;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+void
+udf_itimes(struct udf_node *udf_node, struct timespec *acc,
+       struct timespec *mod, struct timespec *birth)
+{
+       struct timespec now;
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       struct filetimes_extattr_entry *ft_extattr;
+       struct timestamp *atime, *mtime, *attrtime, *ctime;
+       struct timestamp  fe_ctime;
+       struct timespec   cur_birth;
+       uint32_t offset, a_l;
+       uint8_t *filedata;
+       int error;
+
+       /* protect against rogue values */
+       if (!udf_node)
+               return;
+
+       fe  = udf_node->fe;
+       efe = udf_node->efe;
+
+       if (!(udf_node->i_flags & (IN_ACCESS|IN_CHANGE|IN_UPDATE|IN_MODIFY)))
+               return;
+
+       /* get descriptor information */
+       if (fe) {
+               atime    = &fe->atime;
+               mtime    = &fe->mtime;
+               attrtime = &fe->attrtime;
+               filedata = fe->data;
+
+               /* initial save dummy setting */
+               ctime    = &fe_ctime;
+
+               /* check our extended attribute if present */
+               error = udf_extattr_search_intern(udf_node,
+                       UDF_FILETIMES_ATTR_NO, "", &offset, &a_l);
+               if (!error) {
+                       ft_extattr = (struct filetimes_extattr_entry *)
+                               (filedata + offset);
+                       if (ft_extattr->existence & UDF_FILETIMES_FILE_CREATION)
+                               ctime = &ft_extattr->times[0];
+               }
+               /* TODO create the extended attribute if not found ? */
+       } else {
+               assert(udf_node->efe);
+               atime    = &efe->atime;
+               mtime    = &efe->mtime;
+               attrtime = &efe->attrtime;
+               ctime    = &efe->ctime;
+       }
+
+       vfs_timestamp(&now);
+
+       /* set access time */
+       if (udf_node->i_flags & IN_ACCESS) {
+               if (acc == NULL)
+                       acc = &now;
+               udf_timespec_to_timestamp(acc, atime);
+       }
+
+       /* set modification time */
+       if (udf_node->i_flags & (IN_UPDATE | IN_MODIFY)) {
+               if (mod == NULL)
+                       mod = &now;
+               udf_timespec_to_timestamp(mod, mtime);
+
+               /* ensure birthtime is older than set modification! */
+               udf_timestamp_to_timespec(udf_node->ump, ctime, &cur_birth);
+               if ((cur_birth.tv_sec > mod->tv_sec) ||
+                         ((cur_birth.tv_sec == mod->tv_sec) &&
+                            (cur_birth.tv_nsec > mod->tv_nsec))) {
+                       udf_timespec_to_timestamp(mod, ctime);
+               }
+       }
+
+       /* update birthtime if specified */
+       /* XXX we assume here that given birthtime is older than mod */
+       if (birth && (birth->tv_sec != VNOVAL)) {
+               udf_timespec_to_timestamp(birth, ctime);
+       }
+
+       /* set change time */
+       if (udf_node->i_flags & (IN_CHANGE | IN_MODIFY))
+               udf_timespec_to_timestamp(&now, attrtime);
+
+       /* notify updates to the node itself */
+       if (udf_node->i_flags & (IN_ACCESS | IN_MODIFY))
+               udf_node->i_flags |= IN_ACCESSED;
+       if (udf_node->i_flags & (IN_UPDATE | IN_CHANGE))
+               udf_node->i_flags |= IN_MODIFIED;
+
+       /* clear modification flags */
+       udf_node->i_flags &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY);
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_update(struct vnode *vp, struct timespec *acc,
+       struct timespec *mod, struct timespec *birth, int updflags)
+{
+       union dscrptr *dscrptr;
+       struct udf_node  *udf_node = VTOI(vp);
+       struct udf_mount *ump = udf_node->ump;
+       struct regid     *impl_id;
+       int mnt_async = (vp->v_mount->mnt_flag & MNT_ASYNC);
+       int waitfor, flags;
+
+#ifdef DEBUG
+       char bits[128];
+       DPRINTF(CALL, ("udf_update(node, %p, %p, %p, %d)\n", acc, mod, birth,
+               updflags));
+       snprintb(bits, sizeof(bits), IN_FLAGBITS, udf_node->i_flags);
+       DPRINTF(CALL, ("\tnode flags %s\n", bits));
+       DPRINTF(CALL, ("\t\tmnt_async = %d\n", mnt_async));
+#endif
+
+       /* set our times */
+       udf_itimes(udf_node, acc, mod, birth);
+
+       /* set our implementation id */
+       if (udf_node->fe) {
+               dscrptr = (union dscrptr *) udf_node->fe;
+               impl_id = &udf_node->fe->imp_id;
+       } else {
+               dscrptr = (union dscrptr *) udf_node->efe;
+               impl_id = &udf_node->efe->imp_id;
+       }
+
+       /* set our ID */
+       udf_set_regid(impl_id, IMPL_NAME);
+       udf_add_impl_regid(ump, impl_id);
+
+       /* update our crc! on RMW we are not allowed to change a thing */
+       udf_validate_tag_and_crc_sums(dscrptr);
+
+       /* if called when mounted readonly, never write back */
+       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+               return 0;
+
+       /* check if the node is dirty 'enough'*/
+       if (updflags & UPDATE_CLOSE) {
+               flags = udf_node->i_flags & (IN_MODIFIED | IN_ACCESSED);
+       } else {
+               flags = udf_node->i_flags & IN_MODIFIED;
+       }
+       if (flags == 0)
+               return 0;
+
+       /* determine if we need to write sync or async */
+       waitfor = 0;
+       if ((flags & IN_MODIFIED) && (mnt_async == 0)) {
+               /* sync mounted */
+               waitfor = updflags & UPDATE_WAIT;
+               if (updflags & UPDATE_DIROP)
+                       waitfor |= UPDATE_WAIT;
+       }
+       if (waitfor)
+               return VOP_FSYNC(vp, FSCRED, FSYNC_WAIT, 0,0);
+
+       return 0;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+
+/*
+ * Read one fid and process it into a dirent and advance to the next (*fid)
+ * has to be allocated a logical block in size, (*dirent) struct dirent length
+ */
+
+int
+udf_read_fid_stream(struct vnode *vp, uint64_t *offset,
+               struct fileid_desc *fid, struct dirent *dirent)
+{
+       struct udf_node  *dir_node = VTOI(vp);
+       struct udf_mount *ump = dir_node->ump;
+       struct file_entry    *fe  = dir_node->fe;
+       struct extfile_entry *efe = dir_node->efe;
+       uint32_t      fid_size, lb_size;
+       uint64_t      file_size;
+       char         *fid_name;
+       int           enough, error;
+
+       assert(fid);
+       assert(dirent);
+       assert(dir_node);
+       assert(offset);
+       assert(*offset != 1);
+
+       DPRINTF(FIDS, ("read_fid_stream called at offset %"PRIu64"\n", *offset));
+       /* check if we're past the end of the directory */
+       if (fe) {
+               file_size = udf_rw64(fe->inf_len);
+       } else {
+               assert(dir_node->efe);
+               file_size = udf_rw64(efe->inf_len);
+       }
+       if (*offset >= file_size)
+               return EINVAL;
+
+       /* get maximum length of FID descriptor */
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+
+       /* initialise return values */
+       fid_size = 0;
+       memset(dirent, 0, sizeof(struct dirent));
+       memset(fid, 0, lb_size);
+
+       enough  = (file_size - (*offset) >= UDF_FID_SIZE);
+       if (!enough) {
+               /* short dir ... */
+               return EIO;
+       }
+
+       error = vn_rdwr(UIO_READ, vp,
+                       fid, MIN(file_size - (*offset), lb_size), *offset,
+                       UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, FSCRED,
+                       NULL, NULL);
+       if (error)
+               return error;
+
+       DPRINTF(FIDS, ("\tfid piece read in fine\n"));
+       /*
+        * Check if we got a whole descriptor.
+        * TODO Try to `resync' directory stream when something is very wrong.
+        */
+
+       /* check if our FID header is OK */
+       error = udf_check_tag(fid);
+       if (error) {
+               goto brokendir;
+       }
+       DPRINTF(FIDS, ("\ttag check ok\n"));
+
+       if (udf_rw16(fid->tag.id) != TAGID_FID) {
+               error = EIO;
+               goto brokendir;
+       }
+       DPRINTF(FIDS, ("\ttag checked ok: got TAGID_FID\n"));
+
+       /* check for length */
+       fid_size = udf_fidsize(fid);
+       enough = (file_size - (*offset) >= fid_size);
+       if (!enough) {
+               error = EIO;
+               goto brokendir;
+       }
+       DPRINTF(FIDS, ("\tthe complete fid is read in\n"));
+
+       /* check FID contents */
+       error = udf_check_tag_payload((union dscrptr *) fid, lb_size);
+brokendir:
+       if (error) {
+               /* note that is sometimes a bit quick to report */
+               printf("UDF: BROKEN DIRECTORY ENTRY\n");
+               /* RESYNC? */
+               /* TODO: use udf_resync_fid_stream */
+               return EIO;
+       }
+       DPRINTF(FIDS, ("\tpayload checked ok\n"));
+
+       /* we got a whole and valid descriptor! */
+       DPRINTF(FIDS, ("\tinterpret FID\n"));
+
+       /* create resulting dirent structure */
+       fid_name = (char *) fid->data + udf_rw16(fid->l_iu);
+       udf_to_unix_name(dirent->d_name, NAME_MAX,
+               fid_name, fid->l_fi, &ump->logical_vol->desc_charset);
+
+       /* '..' has no name, so provide one */
+       if (fid->file_char & UDF_FILE_CHAR_PAR)
+               strcpy(dirent->d_name, "..");
+
+       dirent->d_fileno = udf_get_node_id(&fid->icb);  /* inode hash XXX */
+       dirent->d_namlen = strlen(dirent->d_name);
+       dirent->d_reclen = _DIRENT_SIZE(dirent);
+
+       /*
+        * Note that its not worth trying to go for the filetypes now... its
+        * too expensive too
+        */
+       dirent->d_type = DT_UNKNOWN;
+
+       /* initial guess for filetype we can make */
+       if (fid->file_char & UDF_FILE_CHAR_DIR)
+               dirent->d_type = DT_DIR;
+
+       /* advance */
+       *offset += fid_size;
+
+       return error;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static void
+udf_sync_pass(struct udf_mount *ump, kauth_cred_t cred, int waitfor,
+       int pass, int *ndirty)
+{
+       struct udf_node *udf_node, *n_udf_node;
+       struct vnode *vp;
+       int vdirty, error;
+       int on_type, on_flags, on_vnode;
+
+derailed:
+       KASSERT(mutex_owned(&mntvnode_lock));
+
+       DPRINTF(SYNC, ("sync_pass %d\n", pass));
+       udf_node = RB_TREE_MIN(&ump->udf_node_tree);
+       for (;udf_node; udf_node = n_udf_node) {
+               DPRINTF(SYNC, ("."));
+
+               udf_node->i_flags &= ~IN_SYNCED;
+               vp = udf_node->vnode;
+
+               mutex_enter(vp->v_interlock);
+               n_udf_node = rb_tree_iterate(&ump->udf_node_tree,
+                   udf_node, RB_DIR_RIGHT);
+
+               if (n_udf_node)
+                       n_udf_node->i_flags |= IN_SYNCED;
+
+               /* system nodes are not synced this way */
+               if (vp->v_vflag & VV_SYSTEM) {
+                       mutex_exit(vp->v_interlock);
+                       continue;
+               }
+
+               /* check if its dirty enough to even try */
+               on_type  = (waitfor == MNT_LAZY || vp->v_type == VNON);
+               on_flags = ((udf_node->i_flags &
+                       (IN_ACCESSED | IN_UPDATE | IN_MODIFIED)) == 0);
+               on_vnode = LIST_EMPTY(&vp->v_dirtyblkhd)
+                       && UVM_OBJ_IS_CLEAN(&vp->v_uobj);
+               if (on_type || (on_flags || on_vnode)) { /* XXX */
+                       /* not dirty (enough?) */
+                       mutex_exit(vp->v_interlock);
+                       continue;
+               }
+
+               mutex_exit(&mntvnode_lock);
+               error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT);
+               if (error) {
+                       mutex_enter(&mntvnode_lock);
+                       if (error == ENOENT)
+                               goto derailed;
+                       *ndirty += 1;
+                       continue;
+               }
+
+               switch (pass) {
+               case 1:
+                       VOP_FSYNC(vp, cred, 0 | FSYNC_DATAONLY,0,0);
+                       break;
+               case 2:
+                       vdirty = vp->v_numoutput;
+                       if (vp->v_tag == VT_UDF)
+                               vdirty += udf_node->outstanding_bufs +
+                                       udf_node->outstanding_nodedscr;
+                       if (vdirty == 0)
+                               VOP_FSYNC(vp, cred, 0,0,0);
+                       *ndirty += vdirty;
+                       break;
+               case 3:
+                       vdirty = vp->v_numoutput;
+                       if (vp->v_tag == VT_UDF)
+                               vdirty += udf_node->outstanding_bufs +
+                                       udf_node->outstanding_nodedscr;
+                       *ndirty += vdirty;
+                       break;
+               }
+
+               vput(vp);
+               mutex_enter(&mntvnode_lock);
+       }
+       DPRINTF(SYNC, ("END sync_pass %d\n", pass));
+}
+
+
+void
+udf_do_sync(struct udf_mount *ump, kauth_cred_t cred, int waitfor)
+{
+       int dummy, ndirty;
+
+       mutex_enter(&mntvnode_lock);
+recount:
+       dummy = 0;
+       DPRINTF(CALL, ("issue VOP_FSYNC(DATA only) on all nodes\n"));
+       DPRINTF(SYNC, ("issue VOP_FSYNC(DATA only) on all nodes\n"));
+       udf_sync_pass(ump, cred, waitfor, 1, &dummy);
+
+       DPRINTF(CALL, ("issue VOP_FSYNC(COMPLETE) on all finished nodes\n"));
+       DPRINTF(SYNC, ("issue VOP_FSYNC(COMPLETE) on all finished nodes\n"));
+       udf_sync_pass(ump, cred, waitfor, 2, &dummy);
+
+       if (waitfor == MNT_WAIT) {
+               ndirty = ump->devvp->v_numoutput;
+               DPRINTF(SYNC, ("counting pending blocks: on devvp %d\n",
+                       ndirty));
+               udf_sync_pass(ump, cred, waitfor, 3, &ndirty);
+               DPRINTF(SYNC, ("counted num dirty pending blocks %d\n",
+                       ndirty));
+       
+               if (ndirty) {
+                       /* 1/4 second wait */
+                       cv_timedwait(&ump->dirtynodes_cv, &mntvnode_lock,
+                               hz/4);
+                       goto recount;
+               }
+       }
+
+       mutex_exit(&mntvnode_lock);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Read and write file extent in/from the buffer.
+ *
+ * The splitup of the extent into seperate request-buffers is to minimise
+ * copying around as much as possible.
+ *
+ * block based file reading and writing
+ */
+
+static int
+udf_read_internal(struct udf_node *node, uint8_t *blob)
+{
+       struct udf_mount *ump;
+       struct file_entry     *fe = node->fe;
+       struct extfile_entry *efe = node->efe;
+       uint64_t inflen;
+       uint32_t sector_size;
+       uint8_t  *pos;
+       int icbflags, addr_type;
+
+       /* get extent and do some paranoia checks */
+       ump = node->ump;
+       sector_size = ump->discinfo.sector_size;
+
+       if (fe) {
+               inflen   = udf_rw64(fe->inf_len);
+               pos      = &fe->data[0] + udf_rw32(fe->l_ea);
+               icbflags = udf_rw16(fe->icbtag.flags);
+       } else {
+               assert(node->efe);
+               inflen   = udf_rw64(efe->inf_len);
+               pos      = &efe->data[0] + udf_rw32(efe->l_ea);
+               icbflags = udf_rw16(efe->icbtag.flags);
+       }
+       addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       assert(addr_type == UDF_ICB_INTERN_ALLOC);
+       assert(inflen < sector_size);
+
+       /* copy out info */
+       memset(blob, 0, sector_size);
+       memcpy(blob, pos, inflen);
+
+       return 0;
+}
+
+
+static int
+udf_write_internal(struct udf_node *node, uint8_t *blob)
+{
+       struct udf_mount *ump;
+       struct file_entry     *fe = node->fe;
+       struct extfile_entry *efe = node->efe;
+       uint64_t inflen;
+       uint32_t sector_size;
+       uint8_t  *pos;
+       int icbflags, addr_type;
+
+       /* get extent and do some paranoia checks */
+       ump = node->ump;
+       sector_size = ump->discinfo.sector_size;
+
+       if (fe) {
+               inflen   = udf_rw64(fe->inf_len);
+               pos      = &fe->data[0] + udf_rw32(fe->l_ea);
+               icbflags = udf_rw16(fe->icbtag.flags);
+       } else {
+               assert(node->efe);
+               inflen   = udf_rw64(efe->inf_len);
+               pos      = &efe->data[0] + udf_rw32(efe->l_ea);
+               icbflags = udf_rw16(efe->icbtag.flags);
+       }
+       addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+       assert(addr_type == UDF_ICB_INTERN_ALLOC);
+       assert(inflen < sector_size);
+
+       /* copy in blob */
+       /* memset(pos, 0, inflen); */
+       memcpy(pos, blob, inflen);
+
+       return 0;
+}
+
+
+void
+udf_read_filebuf(struct udf_node *udf_node, struct buf *buf)
+{
+       struct buf *nestbuf;
+       struct udf_mount *ump = udf_node->ump;
+       uint64_t   *mapping;
+       uint64_t    run_start;
+       uint32_t    sector_size;
+       uint32_t    buf_offset, sector, rbuflen, rblk;
+       uint32_t    from, lblkno;
+       uint32_t    sectors;
+       uint8_t    *buf_pos;
+       int error, run_length, what;
+
+       sector_size = udf_node->ump->discinfo.sector_size;
+
+       from    = buf->b_blkno;
+       sectors = buf->b_bcount / sector_size;
+
+       what = udf_get_c_type(udf_node);
+
+       /* assure we have enough translation slots */
+       KASSERT(buf->b_bcount / sector_size <= UDF_MAX_MAPPINGS);
+       KASSERT(MAXPHYS / sector_size <= UDF_MAX_MAPPINGS);
+
+       if (sectors > UDF_MAX_MAPPINGS) {
+               printf("udf_read_filebuf: implementation limit on bufsize\n");
+               buf->b_error  = EIO;
+               biodone(buf);
+               return;
+       }
+
+       mapping = malloc(sizeof(*mapping) * UDF_MAX_MAPPINGS, M_TEMP, M_WAITOK);
+
+       error = 0;
+       DPRINTF(READ, ("\ttranslate %d-%d\n", from, sectors));
+       error = udf_translate_file_extent(udf_node, from, sectors, mapping);
+       if (error) {
+               buf->b_error  = error;
+               biodone(buf);
+               goto out;
+       }
+       DPRINTF(READ, ("\ttranslate extent went OK\n"));
+
+       /* pre-check if its an internal */
+       if (*mapping == UDF_TRANS_INTERN) {
+               error = udf_read_internal(udf_node, (uint8_t *) buf->b_data);
+               if (error)
+                       buf->b_error  = error;
+               biodone(buf);
+               goto out;
+       }
+       DPRINTF(READ, ("\tnot intern\n"));
+
+#ifdef DEBUG
+       if (udf_verbose & UDF_DEBUG_TRANSLATE) {
+               printf("Returned translation table:\n");
+               for (sector = 0; sector < sectors; sector++) {
+                       printf("%d : %"PRIu64"\n", sector, mapping[sector]);
+               }
+       }
+#endif
+
+       /* request read-in of data from disc sheduler */
+       buf->b_resid = buf->b_bcount;
+       for (sector = 0; sector < sectors; sector++) {
+               buf_offset = sector * sector_size;
+               buf_pos    = (uint8_t *) buf->b_data + buf_offset;
+               DPRINTF(READ, ("\tprocessing rel sector %d\n", sector));
+
+               /* check if its zero or unmapped to stop reading */
+               switch (mapping[sector]) {
+               case UDF_TRANS_UNMAPPED:
+               case UDF_TRANS_ZERO:
+                       /* copy zero sector TODO runlength like below */
+                       memset(buf_pos, 0, sector_size);
+                       DPRINTF(READ, ("\treturning zero sector\n"));
+                       nestiobuf_done(buf, sector_size, 0);
+                       break;
+               default :
+                       DPRINTF(READ, ("\tread sector "
+                           "%"PRIu64"\n", mapping[sector]));
+
+                       lblkno = from + sector;
+                       run_start  = mapping[sector];
+                       run_length = 1;
+                       while (sector < sectors-1) {
+                               if (mapping[sector+1] != mapping[sector]+1)
+                                       break;
+                               run_length++;
+                               sector++;
+                       }
+
+                       /*
+                        * nest an iobuf and mark it for async reading. Since
+                        * we're using nested buffers, they can't be cached by
+                        * design.
+                        */
+                       rbuflen = run_length * sector_size;
+                       rblk    = run_start  * (sector_size/DEV_BSIZE);
+
+                       nestbuf = getiobuf(NULL, true);
+                       nestiobuf_setup(buf, nestbuf, buf_offset, rbuflen);
+                       /* nestbuf is B_ASYNC */
+
+                       /* identify this nestbuf */
+                       nestbuf->b_lblkno   = lblkno;
+                       assert(nestbuf->b_vp == udf_node->vnode);
+
+                       /* CD shedules on raw blkno */
+                       nestbuf->b_blkno      = rblk;
+                       nestbuf->b_proc       = NULL;
+                       nestbuf->b_rawblkno   = rblk;
+                       nestbuf->b_udf_c_type = what;
+
+                       udf_discstrat_queuebuf(ump, nestbuf);
+               }
+       }
+out:
+       /* if we're synchronously reading, wait for the completion */
+       if ((buf->b_flags & B_ASYNC) == 0)
+               biowait(buf);
+
+       DPRINTF(READ, ("\tend of read_filebuf\n"));
+       free(mapping, M_TEMP);
+       return;
+}
+
+
+void
+udf_write_filebuf(struct udf_node *udf_node, struct buf *buf)
+{
+       struct buf *nestbuf;
+       struct udf_mount *ump = udf_node->ump;
+       uint64_t   *mapping;
+       uint64_t    run_start;
+       uint32_t    lb_size;
+       uint32_t    buf_offset, lb_num, rbuflen, rblk;
+       uint32_t    from, lblkno;
+       uint32_t    num_lb;
+       int error, run_length, what, s;
+
+       lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
+
+       from   = buf->b_blkno;
+       num_lb = buf->b_bcount / lb_size;
+
+       what = udf_get_c_type(udf_node);
+
+       /* assure we have enough translation slots */
+       KASSERT(buf->b_bcount / lb_size <= UDF_MAX_MAPPINGS);
+       KASSERT(MAXPHYS / lb_size <= UDF_MAX_MAPPINGS);
+
+       if (num_lb > UDF_MAX_MAPPINGS) {
+               printf("udf_write_filebuf: implementation limit on bufsize\n");
+               buf->b_error  = EIO;
+               biodone(buf);
+               return;
+       }
+
+       mapping = malloc(sizeof(*mapping) * UDF_MAX_MAPPINGS, M_TEMP, M_WAITOK);
+
+       error = 0;
+       DPRINTF(WRITE, ("\ttranslate %d-%d\n", from, num_lb));
+       error = udf_translate_file_extent(udf_node, from, num_lb, mapping);
+       if (error) {
+               buf->b_error  = error;
+               biodone(buf);
+               goto out;
+       }
+       DPRINTF(WRITE, ("\ttranslate extent went OK\n"));
+
+       /* if its internally mapped, we can write it in the descriptor itself */
+       if (*mapping == UDF_TRANS_INTERN) {
+               /* TODO paranoia check if we ARE going to have enough space */
+               error = udf_write_internal(udf_node, (uint8_t *) buf->b_data);
+               if (error)
+                       buf->b_error  = error;
+               biodone(buf);
+               goto out;
+       }
+       DPRINTF(WRITE, ("\tnot intern\n"));
+
+       /* request write out of data to disc sheduler */
+       buf->b_resid = buf->b_bcount;
+       for (lb_num = 0; lb_num < num_lb; lb_num++) {
+               buf_offset = lb_num * lb_size;
+               DPRINTF(WRITE, ("\tprocessing rel lb_num %d\n", lb_num));
+
+               /*
+                * Mappings are not that important here. Just before we write
+                * the lb_num we late-allocate them when needed and update the
+                * mapping in the udf_node.
+                */
+
+               /* XXX why not ignore the mapping altogether ? */
+               DPRINTF(WRITE, ("\twrite lb_num "
+                   "%"PRIu64, mapping[lb_num]));
+
+               lblkno = from + lb_num;
+               run_start  = mapping[lb_num];
+               run_length = 1;
+               while (lb_num < num_lb-1) {
+                       if (mapping[lb_num+1] != mapping[lb_num]+1)
+                               if (mapping[lb_num+1] != mapping[lb_num])
+                                       break;
+                       run_length++;
+                       lb_num++;
+               }
+               DPRINTF(WRITE, ("+ %d\n", run_length));
+
+               /* nest an iobuf on the master buffer for the extent */
+               rbuflen = run_length * lb_size;
+               rblk = run_start * (lb_size/DEV_BSIZE);
+
+               nestbuf = getiobuf(NULL, true);
+               nestiobuf_setup(buf, nestbuf, buf_offset, rbuflen);
+               /* nestbuf is B_ASYNC */
+
+               /* identify this nestbuf */
+               nestbuf->b_lblkno   = lblkno;
+               KASSERT(nestbuf->b_vp == udf_node->vnode);
+
+               /* CD shedules on raw blkno */
+               nestbuf->b_blkno      = rblk;
+               nestbuf->b_proc       = NULL;
+               nestbuf->b_rawblkno   = rblk;
+               nestbuf->b_udf_c_type = what;
+
+               /* increment our outstanding bufs counter */
+               s = splbio();
+                       udf_node->outstanding_bufs++;
+               splx(s);
+
+               udf_discstrat_queuebuf(ump, nestbuf);
+       }
+out:
+       /* if we're synchronously writing, wait for the completion */
+       if ((buf->b_flags & B_ASYNC) == 0)
+               biowait(buf);
+
+       DPRINTF(WRITE, ("\tend of write_filebuf\n"));
+       free(mapping, M_TEMP);
+       return;
+}
+
+/* --------------------------------------------------------------------- */
diff --git a/sys/fs/udf/udf_subr.h b/sys/fs/udf/udf_subr.h
new file mode 100644 (file)
index 0000000..7146c13
--- /dev/null
@@ -0,0 +1,229 @@
+/* $NetBSD: udf_subr.h,v 1.19 2013/07/07 19:49:44 reinoud Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#ifndef _FS_UDF_UDF_SUBR_H_
+#define _FS_UDF_UDF_SUBR_H_
+
+/* handies */
+#define        VFSTOUDF(mp)    ((struct udf_mount *)mp->mnt_data)
+
+
+/* device information updating */
+int udf_update_trackinfo(struct udf_mount *ump, struct mmc_trackinfo *trackinfo);
+int udf_update_discinfo(struct udf_mount *ump);
+int udf_search_tracks(struct udf_mount *ump, struct udf_args *args,
+                 int *first_tracknr, int *last_tracknr);
+int udf_search_writing_tracks(struct udf_mount *ump);
+int udf_setup_writeparams(struct udf_mount *ump);
+int udf_synchronise_caches(struct udf_mount *ump);
+
+/* tags operations */
+int udf_fidsize(struct fileid_desc *fid);
+int udf_check_tag(void *blob);
+int udf_check_tag_payload(void *blob, uint32_t max_length);
+void udf_validate_tag_sum(void *blob);
+void udf_validate_tag_and_crc_sums(void *blob);
+int udf_tagsize(union dscrptr *dscr, uint32_t udf_sector_size);
+
+/* read/write descriptors */
+int udf_read_phys_sectors(struct udf_mount *ump, int what, void *blob,
+       uint32_t start, uint32_t sectors);
+int udf_write_phys_sectors(struct udf_mount *ump, int what, void *blob,
+       uint32_t start, uint32_t sectors);
+int udf_read_phys_dscr(
+               struct udf_mount *ump,
+               uint32_t sector,
+               struct malloc_type *mtype,              /* where to allocate */
+               union dscrptr **dstp);                  /* out */
+
+int udf_write_phys_dscr_sync(struct udf_mount *ump, struct udf_node *udf_node,
+               int what, union dscrptr *dscr,
+               uint32_t sector, uint32_t logsector);
+int udf_write_phys_dscr_async(struct udf_mount *ump, struct udf_node *udf_node,
+                     int what, union dscrptr *dscr,
+                     uint32_t sector, uint32_t logsector,
+                     void (*dscrwr_callback)(struct buf *));
+
+/* read/write node descriptors */
+int udf_create_logvol_dscr(struct udf_mount *ump, struct udf_node *udf_node,
+       struct long_ad *icb, union dscrptr **dscrptr);
+void udf_free_logvol_dscr(struct udf_mount *ump, struct long_ad *icb_loc,
+       void *dscr);
+int udf_read_logvol_dscr(struct udf_mount *ump, struct long_ad *icb,
+       union dscrptr **dscrptr);
+int udf_write_logvol_dscr(struct udf_node *udf_node, union dscrptr *dscr,
+       struct long_ad *icb, int waitfor);
+
+
+/* volume descriptors readers and checkers */
+int udf_read_anchors(struct udf_mount *ump);
+int udf_read_vds_space(struct udf_mount *ump);
+int udf_process_vds(struct udf_mount *ump);
+int udf_read_vds_tables(struct udf_mount *ump);
+int udf_read_rootdirs(struct udf_mount *ump);
+
+/* open/close and sync volumes */
+int udf_open_logvol(struct udf_mount *ump);
+int udf_close_logvol(struct udf_mount *ump, int mntflags);
+int udf_writeout_vat(struct udf_mount *ump);
+int udf_write_physical_partition_spacetables(struct udf_mount *ump, int waitfor);
+int udf_write_metadata_partition_spacetable(struct udf_mount *ump, int waitfor);
+void udf_do_sync(struct udf_mount *ump, kauth_cred_t cred, int waitfor);
+void udf_synchronise_metadatamirror_node(struct udf_mount *ump);
+
+/* translation services */
+int udf_translate_vtop(struct udf_mount *ump, struct long_ad *icb_loc,
+               uint32_t *lb_numres, uint32_t *extres);
+void udf_translate_vtop_list(struct udf_mount *ump, uint32_t sectors,
+               uint16_t vpart_num, uint64_t *lmapping, uint64_t *pmapping);
+int udf_translate_file_extent(struct udf_node *node,
+               uint32_t from, uint32_t num_lb, uint64_t *map);
+void udf_get_adslot(struct udf_node *udf_node, int slot, struct long_ad *icb, int *eof);
+int udf_append_adslot(struct udf_node *udf_node, int *slot, struct long_ad *icb);
+
+int udf_vat_read(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset);
+int udf_vat_write(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset);
+
+/* disc allocation */
+int udf_get_c_type(struct udf_node *udf_node);
+int udf_get_record_vpart(struct udf_mount *ump, int udf_c_type);
+void udf_do_reserve_space(struct udf_mount *ump, struct udf_node *udf_node, uint16_t vpart_num, uint32_t num_lb);
+void udf_do_unreserve_space(struct udf_mount *ump, struct udf_node *udf_node, uint16_t vpart_num, uint32_t num_lb);
+int udf_reserve_space(struct udf_mount *ump, struct udf_node *udf_node, int udf_c_type, uint16_t vpart_num, uint32_t num_lb, int can_fail);
+void udf_cleanup_reservation(struct udf_node *udf_node);
+int udf_allocate_space(struct udf_mount *ump, struct udf_node *udf_node, int udf_c_type, uint16_t vpart_num, uint32_t num_lb, uint64_t *lmapping);
+void udf_free_allocated_space(struct udf_mount *ump, uint32_t lb_num, uint16_t vpart_num, uint32_t num_lb);
+void udf_late_allocate_buf(struct udf_mount *ump, struct buf *buf, uint64_t *lmapping, struct long_ad *node_ad_cpy, uint16_t *vpart_num);
+int udf_grow_node(struct udf_node *node, uint64_t new_size);
+int udf_shrink_node(struct udf_node *node, uint64_t new_size);
+void udf_calc_freespace(struct udf_mount *ump, uint64_t *sizeblks, uint64_t *freeblks);
+
+/* node readers and writers */
+uint64_t udf_advance_uniqueid(struct udf_mount *ump);
+
+#define UDF_LOCK_NODE(udf_node, flag) udf_lock_node(udf_node, (flag), __FILE__, __LINE__)
+#define UDF_UNLOCK_NODE(udf_node, flag) udf_unlock_node(udf_node, (flag))
+void udf_lock_node(struct udf_node *udf_node, int flag, char const *fname, const int lineno);
+void udf_unlock_node(struct udf_node *udf_node, int flag);
+
+int udf_get_node(struct udf_mount *ump, struct long_ad *icbloc, struct udf_node **noderes);
+int udf_writeout_node(struct udf_node *udf_node, int waitfor);
+int udf_dispose_node(struct udf_node *node);
+
+/* node ops */
+int udf_resize_node(struct udf_node *node, uint64_t new_size, int *extended);
+int udf_extattr_search_intern(struct udf_node *node, uint32_t sattr, char const *sattrname, uint32_t *offsetp, uint32_t *lengthp);
+
+/* node data buffer read/write */
+void udf_read_filebuf(struct udf_node *node, struct buf *buf);
+void udf_write_filebuf(struct udf_node *node, struct buf *buf);
+void udf_fixup_fid_block(uint8_t *blob, int lb_size, int rfix_pos, int max_rfix_pos, uint32_t lb_num);
+void udf_fixup_internal_extattr(uint8_t *blob, uint32_t lb_num);
+void udf_fixup_node_internals(struct udf_mount *ump, uint8_t *blob, int udf_c_type);
+
+/* device strategy */
+void udf_discstrat_init(struct udf_mount *ump);
+void udf_discstrat_finish(struct udf_mount *ump);
+void udf_discstrat_queuebuf(struct udf_mount *ump, struct buf *nestbuf);
+
+/* structure writers */
+int udf_write_terminator(struct udf_mount *ump, uint32_t sector);
+
+/* structure creators */
+void udf_inittag(struct udf_mount *ump, struct desc_tag *tag, int tagid, uint32_t sector);
+void udf_set_regid(struct regid *regid, char const *name);
+void udf_add_domain_regid(struct udf_mount *ump, struct regid *regid);
+void udf_add_udf_regid(struct udf_mount *ump, struct regid *regid);
+void udf_add_impl_regid(struct udf_mount *ump, struct regid *regid);
+void udf_add_app_regid(struct udf_mount *ump, struct regid *regid);
+
+/* directory operations and helpers */
+void udf_osta_charset(struct charspec *charspec);
+int udf_read_fid_stream(struct vnode *vp, uint64_t *offset, struct fileid_desc *fid, struct dirent *dirent);
+int udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen, struct long_ad *icb_loc, int *found);
+int udf_create_node(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, struct componentname *cnp);
+void udf_delete_node(struct udf_node *udf_node);
+
+int udf_chsize(struct vnode *vp, u_quad_t newsize, kauth_cred_t cred);
+
+int udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *udf_node, struct componentname *cnp);
+int udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *udf_node, struct vattr *vap, struct componentname *cnp);
+int udf_dir_update_rootentry(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *new_parent_node);
+int udf_dirhash_fill(struct udf_node *dir_node);
+
+/* update and times */
+void udf_add_to_dirtylist(struct udf_node *udf_node);
+void udf_remove_from_dirtylist(struct udf_node *udf_node);
+void udf_itimes(struct udf_node *udf_node, struct timespec *acc,
+       struct timespec *mod, struct timespec *birth);
+int  udf_update(struct vnode *node, struct timespec *acc,
+       struct timespec *mod, struct timespec *birth, int updflags);
+
+/* helpers and converters */
+void udf_init_nodes_tree(struct udf_mount *ump);
+long udf_get_node_id(const struct long_ad *icbptr);    /* for `inode' numbering */
+int udf_compare_icb(const struct long_ad *a, const struct long_ad *b);
+uint32_t udf_getaccessmode(struct udf_node *node);
+void udf_setaccessmode(struct udf_node *udf_node, mode_t mode);
+void udf_getownership(struct udf_node *udf_node, uid_t *uidp, gid_t *gidp);
+void udf_setownership(struct udf_node *udf_node, uid_t uid, gid_t gid);
+
+void udf_to_unix_name(char *result, int result_len, char *id, int len, struct charspec *chsp);
+void unix_to_udf_name(char *result, uint8_t *result_len, char const *name, int name_len, struct charspec *chsp);
+
+void udf_timestamp_to_timespec(struct udf_mount *ump, struct timestamp *timestamp, struct timespec *timespec);
+void udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp);
+
+/* vnode operations */
+int udf_inactive(void *v);
+int udf_reclaim(void *v);
+int udf_readdir(void *v);
+int udf_getattr(void *v);
+int udf_setattr(void *v);
+int udf_pathconf(void *v);
+int udf_open(void *v);
+int udf_close(void *v);
+int udf_access(void *v);
+int udf_read(void *v);
+int udf_write(void *v);
+int udf_trivial_bmap(void *v);
+int udf_vfsstrategy(void *v);
+int udf_lookup(void *v);
+int udf_create(void *v);
+int udf_mknod(void *v);
+int udf_link(void *);
+int udf_symlink(void *v);
+int udf_readlink(void *v);
+int udf_rename(void *v);
+int udf_remove(void *v);
+int udf_mkdir(void *v);
+int udf_rmdir(void *v);
+int udf_fsync(void *v);
+int udf_advlock(void *v);
+
+#endif /* !_FS_UDF_UDF_SUBR_H_ */
diff --git a/sys/fs/udf/udf_vfsops.c b/sys/fs/udf/udf_vfsops.c
new file mode 100644 (file)
index 0000000..7fb4467
--- /dev/null
@@ -0,0 +1,949 @@
+/* $NetBSD: udf_vfsops.c,v 1.64 2013/09/30 18:58:00 hannken Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__KERNEL_RCSID(0, "$NetBSD: udf_vfsops.c,v 1.64 2013/09/30 18:58:00 hannken Exp $");
+#endif /* not lint */
+
+
+#if defined(_KERNEL_OPT)
+#include "opt_compat_netbsd.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs.h>
+#include <miscfs/specfs/specdev.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/module.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+#include <sys/dirhash.h>
+
+#include "udf.h"
+#include "udf_subr.h"
+#include "udf_bswap.h"
+
+MODULE(MODULE_CLASS_VFS, udf, NULL);
+
+#define VTOI(vnode) ((struct udf_node *) vnode->v_data)
+
+/* verbose levels of the udf filingsystem */
+int udf_verbose = UDF_DEBUGGING;
+
+/* malloc regions */
+MALLOC_JUSTDEFINE(M_UDFMNT,   "UDF mount",     "UDF mount structures");
+MALLOC_JUSTDEFINE(M_UDFVOLD,  "UDF volspace",  "UDF volume space descriptors");
+MALLOC_JUSTDEFINE(M_UDFTEMP,  "UDF temp",      "UDF scrap space");
+struct pool udf_node_pool;
+
+/* supported functions predefined */
+VFS_PROTOS(udf);
+
+static struct sysctllog *udf_sysctl_log;
+
+/* internal functions */
+static int udf_mountfs(struct vnode *, struct mount *, struct lwp *, struct udf_args *);
+
+
+/* --------------------------------------------------------------------- */
+
+/* predefine vnode-op list descriptor */
+extern const struct vnodeopv_desc udf_vnodeop_opv_desc;
+
+const struct vnodeopv_desc * const udf_vnodeopv_descs[] = {
+       &udf_vnodeop_opv_desc,
+       NULL,
+};
+
+
+/* vfsops descriptor linked in as anchor point for the filingsystem */
+struct vfsops udf_vfsops = {
+       MOUNT_UDF,                      /* vfs_name */
+       sizeof (struct udf_args),
+       udf_mount,
+       udf_start,
+       udf_unmount,
+       udf_root,
+       (void *)eopnotsupp,             /* vfs_quotactl */
+       udf_statvfs,
+       udf_sync,
+       udf_vget,
+       udf_fhtovp,
+       udf_vptofh,
+       udf_init,
+       udf_reinit,
+       udf_done,
+       udf_mountroot,
+       udf_snapshot,
+       vfs_stdextattrctl,
+       (void *)eopnotsupp,             /* vfs_suspendctl */
+       genfs_renamelock_enter,
+       genfs_renamelock_exit,
+       (void *)eopnotsupp,
+       udf_vnodeopv_descs,
+       0, /* int vfs_refcount   */
+       { NULL, NULL, }, /* LIST_ENTRY(vfsops) */
+};
+
+/* --------------------------------------------------------------------- */
+
+/* file system starts here */
+void
+udf_init(void)
+{
+       size_t size;
+
+       /* setup memory types */
+       malloc_type_attach(M_UDFMNT);
+       malloc_type_attach(M_UDFVOLD);
+       malloc_type_attach(M_UDFTEMP);
+
+       /* init node pools */
+       size = sizeof(struct udf_node);
+       pool_init(&udf_node_pool, size, 0, 0, 0,
+               "udf_node_pool", NULL, IPL_NONE);
+}
+
+
+void
+udf_reinit(void)
+{
+       /* nothing to do */
+}
+
+
+void
+udf_done(void)
+{
+       /* remove pools */
+       pool_destroy(&udf_node_pool);
+
+       malloc_type_detach(M_UDFMNT);
+       malloc_type_detach(M_UDFVOLD);
+       malloc_type_detach(M_UDFTEMP);
+}
+
+/*
+ * If running a DEBUG kernel, provide an easy way to set the debug flags when
+ * running into a problem.
+ */
+#define UDF_VERBOSE_SYSCTLOPT        1
+
+static int
+udf_modcmd(modcmd_t cmd, void *arg)
+{
+       const struct sysctlnode *node;
+       int error;
+
+       switch (cmd) {
+       case MODULE_CMD_INIT:
+               error = vfs_attach(&udf_vfsops);
+               if (error != 0)
+                       break;
+               /*
+                * XXX the "24" below could be dynamic, thereby eliminating one
+                * more instance of the "number to vfs" mapping problem, but
+                * "24" is the order as taken from sys/mount.h
+                */
+               sysctl_createv(&udf_sysctl_log, 0, NULL, NULL,
+                              CTLFLAG_PERMANENT,
+                              CTLTYPE_NODE, "vfs", NULL,
+                              NULL, 0, NULL, 0,
+                              CTL_VFS, CTL_EOL);
+               sysctl_createv(&udf_sysctl_log, 0, NULL, &node,
+                              CTLFLAG_PERMANENT,
+                              CTLTYPE_NODE, "udf",
+                              SYSCTL_DESCR("OSTA Universal File System"),
+                              NULL, 0, NULL, 0,
+                              CTL_VFS, 24, CTL_EOL);
+#ifdef DEBUG
+               sysctl_createv(&udf_sysctl_log, 0, NULL, &node,
+                              CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                              CTLTYPE_INT, "verbose",
+                              SYSCTL_DESCR("Bitmask for filesystem debugging"),
+                              NULL, 0, &udf_verbose, 0,
+                              CTL_VFS, 24, UDF_VERBOSE_SYSCTLOPT, CTL_EOL);
+#endif
+               break;
+       case MODULE_CMD_FINI:
+               error = vfs_detach(&udf_vfsops);
+               if (error != 0)
+                       break;
+               sysctl_teardown(&udf_sysctl_log);
+               break;
+       default:
+               error = ENOTTY;
+               break;
+       }
+
+       return (error);
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_mountroot(void)
+{
+       return EOPNOTSUPP;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define MPFREE(a, lst) \
+       if ((a)) free((a), lst);
+static void
+free_udf_mountinfo(struct mount *mp)
+{
+       struct udf_mount *ump;
+       int i;
+
+       if (!mp)
+               return;
+
+       ump = VFSTOUDF(mp);
+       if (ump) {
+               /* clear our data */
+               for (i = 0; i < UDF_ANCHORS; i++)
+                       MPFREE(ump->anchors[i], M_UDFVOLD);
+               MPFREE(ump->primary_vol,      M_UDFVOLD);
+               MPFREE(ump->logical_vol,      M_UDFVOLD);
+               MPFREE(ump->unallocated,      M_UDFVOLD);
+               MPFREE(ump->implementation,   M_UDFVOLD);
+               MPFREE(ump->logvol_integrity, M_UDFVOLD);
+               for (i = 0; i < UDF_PARTITIONS; i++) {
+                       MPFREE(ump->partitions[i],        M_UDFVOLD);
+                       MPFREE(ump->part_unalloc_dscr[i], M_UDFVOLD);
+                       MPFREE(ump->part_freed_dscr[i],   M_UDFVOLD);
+               }
+               MPFREE(ump->metadata_unalloc_dscr, M_UDFVOLD);
+
+               MPFREE(ump->fileset_desc,   M_UDFVOLD);
+               MPFREE(ump->sparing_table,  M_UDFVOLD);
+
+               MPFREE(ump->la_node_ad_cpy, M_UDFMNT);
+               MPFREE(ump->la_pmapping,    M_TEMP);
+               MPFREE(ump->la_lmapping,    M_TEMP);
+
+               mutex_destroy(&ump->ihash_lock);
+               mutex_destroy(&ump->get_node_lock);
+               mutex_destroy(&ump->logvol_mutex);
+               mutex_destroy(&ump->allocate_mutex);
+               cv_destroy(&ump->dirtynodes_cv);
+
+               MPFREE(ump->vat_table, M_UDFVOLD);
+
+               free(ump, M_UDFMNT);
+       }
+}
+#undef MPFREE
+
+/* --------------------------------------------------------------------- */
+
+/* if the system nodes exist, release them */
+static void
+udf_release_system_nodes(struct mount *mp)
+{
+       struct udf_mount *ump = VFSTOUDF(mp);
+       int error;
+
+       /* if we haven't even got an ump, dont bother */
+       if (!ump)
+               return;
+
+       /* VAT partition support */
+       if (ump->vat_node)
+               vrele(ump->vat_node->vnode);
+
+       /* Metadata partition support */
+       if (ump->metadata_node)
+               vrele(ump->metadata_node->vnode);
+       if (ump->metadatamirror_node)
+               vrele(ump->metadatamirror_node->vnode);
+       if (ump->metadatabitmap_node)
+               vrele(ump->metadatabitmap_node->vnode);
+
+       /* This flush should NOT write anything nor allow any node to remain */
+       if ((error = vflush(ump->vfs_mountp, NULLVP, 0)) != 0)
+               panic("Failure to flush UDF system vnodes\n");
+}
+
+
+int
+udf_mount(struct mount *mp, const char *path,
+         void *data, size_t *data_len)
+{
+       struct lwp *l = curlwp;
+       struct udf_args *args = data;
+       struct udf_mount *ump;
+       struct vnode *devvp;
+       int openflags, accessmode, error;
+
+       DPRINTF(CALL, ("udf_mount called\n"));
+
+       if (*data_len < sizeof *args)
+               return EINVAL;
+
+       if (mp->mnt_flag & MNT_GETARGS) {
+               /* request for the mount arguments */
+               ump = VFSTOUDF(mp);
+               if (ump == NULL)
+                       return EINVAL;
+               *args = ump->mount_args;
+               *data_len = sizeof *args;
+               return 0;
+       }
+
+       /* handle request for updating mount parameters */
+       /* TODO can't update my mountpoint yet */
+       if (mp->mnt_flag & MNT_UPDATE) {
+               return EOPNOTSUPP;
+       }
+
+       /* OK, so we are asked to mount the device */
+
+       /* check/translate struct version */
+       /* TODO sanity checking other mount arguments */
+       if (args->version != 1) {
+               printf("mount_udf: unrecognized argument structure version\n");
+               return EINVAL;
+       }
+
+       /* lookup name to get its vnode */
+       error = namei_simple_user(args->fspec,
+                               NSM_FOLLOW_NOEMULROOT, &devvp);
+       if (error)
+               return error;
+
+#ifdef DEBUG
+       if (udf_verbose & UDF_DEBUG_VOLUMES)
+               vprint("UDF mount, trying to mount \n", devvp);
+#endif
+
+       /* check if its a block device specified */
+       if (devvp->v_type != VBLK) {
+               vrele(devvp);
+               return ENOTBLK;
+       }
+       if (bdevsw_lookup(devvp->v_rdev) == NULL) {
+               vrele(devvp);
+               return ENXIO; 
+       }
+
+       /*
+        * If mount by non-root, then verify that user has necessary
+        * permissions on the device.
+        */
+       accessmode = VREAD;
+       if ((mp->mnt_flag & MNT_RDONLY) == 0)
+               accessmode |= VWRITE;
+       vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
+       error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
+           KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(accessmode));
+       VOP_UNLOCK(devvp);
+       if (error) {
+               vrele(devvp);
+               return error;
+       }
+
+       /*
+        * Open device and try to mount it!
+        */
+       if (mp->mnt_flag & MNT_RDONLY) {
+               openflags = FREAD;
+       } else {
+               openflags = FREAD | FWRITE;
+       }
+       vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
+       error = VOP_OPEN(devvp, openflags, FSCRED);
+       VOP_UNLOCK(devvp);
+       if (error == 0) {
+               /* opened ok, try mounting */
+               error = udf_mountfs(devvp, mp, l, args);
+               if (error) {
+                       udf_release_system_nodes(mp);
+                       /* cleanup */
+                       udf_discstrat_finish(VFSTOUDF(mp));
+                       free_udf_mountinfo(mp);
+                       vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
+                       (void) VOP_CLOSE(devvp, openflags, NOCRED);
+                       VOP_UNLOCK(devvp);
+               }
+       }
+       if (error) {
+               /* devvp is still locked */
+               vrele(devvp);
+               return error;
+       }
+
+       /* register our mountpoint being on this device */
+       spec_node_setmountedfs(devvp, mp);
+
+       /* successfully mounted */
+       DPRINTF(VOLUMES, ("udf_mount() successfull\n"));
+
+       error = set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE,
+                       mp->mnt_op->vfs_name, mp, l);
+       if (error)
+               return error;
+
+       /* If we're not opened read-only, open its logical volume */
+       if ((mp->mnt_flag & MNT_RDONLY) == 0) {
+               if ((error = udf_open_logvol(VFSTOUDF(mp))) != 0) {
+                       printf( "mount_udf: can't open logical volume for "
+                               "writing, downgrading access to read-only\n");
+                       mp->mnt_flag |= MNT_RDONLY;
+                       /* FIXME we can't return error now on open failure */
+                       return 0;
+               }
+       }
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef DEBUG
+static void
+udf_unmount_sanity_check(struct mount *mp)
+{
+       struct vnode *vp;
+
+       printf("On unmount, i found the following nodes:\n");
+       TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
+               vprint("", vp);
+               if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE) {
+                       printf("  is locked\n");
+               }
+               if (vp->v_usecount > 1)
+                       printf("  more than one usecount %d\n", vp->v_usecount);
+       }
+}
+#endif
+
+
+int
+udf_unmount(struct mount *mp, int mntflags)
+{
+       struct udf_mount *ump;
+       int error, flags, closeflags;
+
+       DPRINTF(CALL, ("udf_umount called\n"));
+
+       ump = VFSTOUDF(mp);
+       if (!ump)
+               panic("UDF unmount: empty ump\n");
+
+       flags = (mntflags & MNT_FORCE) ? FORCECLOSE : 0;
+       /* TODO remove these paranoid functions */
+#ifdef DEBUG
+       if (udf_verbose & UDF_DEBUG_LOCKING)
+               udf_unmount_sanity_check(mp);
+#endif
+
+       /*
+        * By specifying SKIPSYSTEM we can skip vnodes marked with VV_SYSTEM.
+        * This hardly documented feature allows us to exempt certain files
+        * from being flushed.
+        */
+       if ((error = vflush(mp, NULLVP, flags | SKIPSYSTEM)) != 0)
+               return error;
+
+       /* update nodes and wait for completion of writeout of system nodes */
+       udf_sync(mp, FSYNC_WAIT, NOCRED);
+
+#ifdef DEBUG
+       if (udf_verbose & UDF_DEBUG_LOCKING)
+               udf_unmount_sanity_check(mp);
+#endif
+
+       /* flush again, to check if we are still busy for something else */
+       if ((error = vflush(ump->vfs_mountp, NULLVP, flags | SKIPSYSTEM)) != 0)
+               return error;
+
+       DPRINTF(VOLUMES, ("flush OK on unmount\n"));
+
+       /* close logical volume and close session if requested */
+       if ((error = udf_close_logvol(ump, mntflags)) != 0)
+               return error;
+
+#ifdef DEBUG
+       DPRINTF(VOLUMES, ("FINAL sanity check\n"));
+       if (udf_verbose & UDF_DEBUG_LOCKING)
+               udf_unmount_sanity_check(mp);
+#endif
+
+       /* NOTE release system nodes should NOT write anything */
+       udf_release_system_nodes(mp);
+
+       /* finalise disc strategy */
+       udf_discstrat_finish(ump);
+
+       /* synchronise device caches */
+       (void) udf_synchronise_caches(ump);
+
+       /* close device */
+       DPRINTF(VOLUMES, ("closing device\n"));
+       if (mp->mnt_flag & MNT_RDONLY) {
+               closeflags = FREAD;
+       } else {
+               closeflags = FREAD | FWRITE;
+       }
+
+       /* devvp is still locked by us */
+       vn_lock(ump->devvp, LK_EXCLUSIVE | LK_RETRY);
+       error = VOP_CLOSE(ump->devvp, closeflags, NOCRED);
+       if (error)
+               printf("Error during closure of device! error %d, "
+                      "device might stay locked\n", error);
+       DPRINTF(VOLUMES, ("device close ok\n"));
+
+       /* clear our mount reference and release device node */
+       spec_node_setmountedfs(ump->devvp, NULL);
+       vput(ump->devvp);
+
+       /* free our ump */
+       free_udf_mountinfo(mp);
+
+       /* free ump struct references */
+       mp->mnt_data = NULL;
+       mp->mnt_flag &= ~MNT_LOCAL;
+
+       DPRINTF(VOLUMES, ("Fin unmount\n"));
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Helper function of udf_mount() that actually mounts the disc.
+ */
+
+static int
+udf_mountfs(struct vnode *devvp, struct mount *mp,
+           struct lwp *l, struct udf_args *args)
+{
+       struct udf_mount     *ump;
+       uint32_t sector_size, lb_size, bshift;
+       uint32_t logvol_integrity;
+       int    num_anchors, error;
+
+       /* flush out any old buffers remaining from a previous use. */
+       if ((error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0)))
+               return error;
+
+       /* setup basic mount information */
+       mp->mnt_data = NULL;
+       mp->mnt_stat.f_fsidx.__fsid_val[0] = (uint32_t) devvp->v_rdev;
+       mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_UDF);
+       mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
+       mp->mnt_stat.f_namemax = UDF_MAXNAMLEN;
+       mp->mnt_flag |= MNT_LOCAL;
+//     mp->mnt_iflag |= IMNT_MPSAFE;
+
+       /* allocate udf part of mount structure; malloc always succeeds */
+       ump = malloc(sizeof(struct udf_mount), M_UDFMNT, M_WAITOK | M_ZERO);
+
+       /* init locks */
+       mutex_init(&ump->logvol_mutex, MUTEX_DEFAULT, IPL_NONE);
+       mutex_init(&ump->ihash_lock, MUTEX_DEFAULT, IPL_NONE);
+       mutex_init(&ump->get_node_lock, MUTEX_DEFAULT, IPL_NONE);
+       mutex_init(&ump->allocate_mutex, MUTEX_DEFAULT, IPL_NONE);
+       cv_init(&ump->dirtynodes_cv, "udfsync2");
+
+       /* init rbtree for nodes, ordered by their icb address (long_ad) */
+       udf_init_nodes_tree(ump);
+
+       /* set up linkage */
+       mp->mnt_data    = ump;
+       ump->vfs_mountp = mp;
+
+       /* set up arguments and device */
+       ump->mount_args = *args;
+       ump->devvp      = devvp;
+       if ((error = udf_update_discinfo(ump))) {
+               printf("UDF mount: error inspecting fs node\n");
+               return error;
+       }
+
+       /* inspect sector size */
+       sector_size = ump->discinfo.sector_size;
+       bshift = 1;
+       while ((1 << bshift) < sector_size)
+               bshift++;
+       if ((1 << bshift) != sector_size) {
+               printf("UDF mount: "
+                      "hit NetBSD implementation fence on sector size\n");
+               return EIO;
+       }
+
+       /* temporary check to overcome sectorsize >= 8192 bytes panic */
+       if (sector_size >= 8192) {
+               printf("UDF mount: "
+                       "hit implementation limit, sectorsize to big\n");
+               return EIO;
+       }
+
+       /*
+        * Inspect if we're asked to mount read-write on a non recordable or
+        * closed sequential disc.
+        */
+       if ((mp->mnt_flag & MNT_RDONLY) == 0) {
+               if ((ump->discinfo.mmc_cur & MMC_CAP_RECORDABLE) == 0) {
+                       printf("UDF mount: disc is not recordable\n");
+                       return EROFS;
+               }
+               if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) {
+                       if (ump->discinfo.disc_state == MMC_STATE_FULL) {
+                               printf("UDF mount: disc is not appendable\n");
+                               return EROFS;
+                       }
+
+                       /*
+                        * TODO if the last session is closed check if there
+                        * is enough space to open/close new session
+                        */
+               }
+               /* double check if we're not mounting a pervious session RW */
+               if (args->sessionnr != 0) {
+                       printf("UDF mount: updating a previous session "
+                               "not yet allowed\n");
+                       return EROFS;
+               }
+       }
+
+       /* initialise bootstrap disc strategy */
+       ump->strategy = &udf_strat_bootstrap;
+       udf_discstrat_init(ump);
+
+       /* read all anchors to get volume descriptor sequence */
+       num_anchors = udf_read_anchors(ump);
+       if (num_anchors == 0)
+               return EINVAL;
+
+       DPRINTF(VOLUMES, ("Read %d anchors on this disc, session %d\n",
+           num_anchors, args->sessionnr));
+
+       /* read in volume descriptor sequence */
+       if ((error = udf_read_vds_space(ump))) {
+               printf("UDF mount: error reading volume space\n");
+               return error;
+       }
+
+       /* close down bootstrap disc strategy */
+       udf_discstrat_finish(ump);
+
+       /* check consistency and completeness */
+       if ((error = udf_process_vds(ump))) {
+               printf( "UDF mount: disc not properly formatted"
+                       "(bad VDS)\n");
+               return error;
+       }
+
+       /* switch to new disc strategy */
+       KASSERT(ump->strategy != &udf_strat_bootstrap);
+       udf_discstrat_init(ump);
+
+       /* initialise late allocation administration space */
+       ump->la_lmapping = malloc(sizeof(uint64_t) * UDF_MAX_MAPPINGS,
+                       M_TEMP, M_WAITOK);
+       ump->la_pmapping = malloc(sizeof(uint64_t) * UDF_MAX_MAPPINGS,
+                       M_TEMP, M_WAITOK);
+
+       /* setup node cleanup extents copy space */
+       lb_size = udf_rw32(ump->logical_vol->lb_size);
+       ump->la_node_ad_cpy = malloc(lb_size * UDF_MAX_ALLOC_EXTENTS,
+               M_UDFMNT, M_WAITOK);
+       memset(ump->la_node_ad_cpy, 0, lb_size * UDF_MAX_ALLOC_EXTENTS);
+
+       /* setup rest of mount information */
+       mp->mnt_data = ump;
+
+       /* bshift is allways equal to disc sector size */
+       mp->mnt_dev_bshift = bshift;
+       mp->mnt_fs_bshift  = bshift;
+
+       /* note that the mp info needs to be initialised for reading! */
+       /* read vds support tables like VAT, sparable etc. */
+       if ((error = udf_read_vds_tables(ump))) {
+               printf( "UDF mount: error in format or damaged disc "
+                       "(VDS tables failing)\n");
+               return error;
+       }
+
+       /* check if volume integrity is closed otherwise its dirty */
+       logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type);
+       if (logvol_integrity != UDF_INTEGRITY_CLOSED) {
+               printf("UDF mount: file system is not clean; ");
+               printf("please fsck(8)\n");
+               return EPERM;
+       }
+
+       /* read root directory */
+       if ((error = udf_read_rootdirs(ump))) {
+               printf( "UDF mount: "
+                       "disc not properly formatted or damaged disc "
+                       "(rootdirs failing)\n");
+               return error;
+       }
+
+       /* success! */
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_start(struct mount *mp, int flags)
+{
+       /* do we have to do something here? */
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_root(struct mount *mp, struct vnode **vpp)
+{
+       struct vnode *vp;
+       struct long_ad *dir_loc;
+       struct udf_mount *ump = VFSTOUDF(mp);
+       struct udf_node *root_dir;
+       int error;
+
+       DPRINTF(CALL, ("udf_root called\n"));
+
+       dir_loc = &ump->fileset_desc->rootdir_icb;
+       error = udf_get_node(ump, dir_loc, &root_dir);
+
+       if (!root_dir)
+               error = ENOENT;
+       if (error)
+               return error;
+
+       vp = root_dir->vnode;
+       KASSERT(vp->v_vflag & VV_ROOT);
+
+       *vpp = vp;
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_statvfs(struct mount *mp, struct statvfs *sbp)
+{
+       struct udf_mount *ump = VFSTOUDF(mp);
+       struct logvol_int_desc *lvid;
+       struct udf_logvol_info *impl;
+       uint64_t freeblks, sizeblks;
+       int num_part;
+
+       DPRINTF(CALL, ("udf_statvfs called\n"));
+       sbp->f_flag   = mp->mnt_flag;
+       sbp->f_bsize  = ump->discinfo.sector_size;
+       sbp->f_frsize = ump->discinfo.sector_size;
+       sbp->f_iosize = ump->discinfo.sector_size;
+
+       mutex_enter(&ump->allocate_mutex);
+
+       udf_calc_freespace(ump, &sizeblks, &freeblks);
+
+       sbp->f_blocks = sizeblks;
+       sbp->f_bfree  = freeblks;
+       sbp->f_files  = 0;
+
+       lvid = ump->logvol_integrity;
+       num_part = udf_rw32(lvid->num_part);
+       impl = (struct udf_logvol_info *) (lvid->tables + 2*num_part);
+       if (impl) {
+               sbp->f_files  = udf_rw32(impl->num_files);
+               sbp->f_files += udf_rw32(impl->num_directories);
+       }
+
+       /* XXX read only for now XXX */
+       sbp->f_bavail = 0;
+       sbp->f_bresvd = 0;
+
+       /* tricky, next only aplies to ffs i think, so set to zero */
+       sbp->f_ffree  = 0;
+       sbp->f_favail = 0;
+       sbp->f_fresvd = 0;
+
+       mutex_exit(&ump->allocate_mutex);
+
+       copy_statvfs_info(sbp, mp);
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * TODO what about writing out free space maps, lvid etc? only on `waitfor'
+ * i.e. explicit syncing by the user?
+ */
+
+static int
+udf_sync_writeout_system_files(struct udf_mount *ump, int clearflags)
+{
+       int error;
+
+       /* XXX lock for VAT en bitmaps? */
+       /* metadata nodes are written synchronous */
+       DPRINTF(CALL, ("udf_sync: syncing metadata\n"));
+       if (ump->lvclose & UDF_WRITE_VAT)
+               udf_writeout_vat(ump);
+
+       error = 0;
+       if (ump->lvclose & UDF_WRITE_PART_BITMAPS) {
+               /* writeout metadata spacetable if existing */
+               error = udf_write_metadata_partition_spacetable(ump, MNT_WAIT);
+               if (error)
+                       printf( "udf_writeout_system_files : "
+                               " writeout of metadata space bitmap failed\n");
+
+               /* writeout partition spacetables */
+               error = udf_write_physical_partition_spacetables(ump, MNT_WAIT);
+               if (error)
+                       printf( "udf_writeout_system_files : "
+                               "writeout of space tables failed\n");
+               if (!error && clearflags)
+                       ump->lvclose &= ~UDF_WRITE_PART_BITMAPS;
+       }
+
+       return error;
+}
+
+
+int
+udf_sync(struct mount *mp, int waitfor, kauth_cred_t cred)
+{
+       struct udf_mount *ump = VFSTOUDF(mp);
+
+       DPRINTF(CALL, ("udf_sync called\n"));
+       /* if called when mounted readonly, just ignore */
+       if (mp->mnt_flag & MNT_RDONLY)
+               return 0;
+
+       if (ump->syncing && !waitfor) {
+               printf("UDF: skipping autosync\n");
+               return 0;
+       }
+
+       /* get sync lock */
+       ump->syncing = 1;
+
+       /* pre-sync */
+       udf_do_sync(ump, cred, waitfor);
+
+       if (waitfor == MNT_WAIT)
+               udf_sync_writeout_system_files(ump, true);
+
+       DPRINTF(CALL, ("end of udf_sync()\n"));
+       ump->syncing = 0;
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Get vnode for the file system type specific file id ino for the fs. Its
+ * used for reference to files by unique ID and for NFSv3.
+ * (optional) TODO lookup why some sources state NFSv3
+ */
+int
+udf_vget(struct mount *mp, ino_t ino,
+    struct vnode **vpp)
+{
+       DPRINTF(NOTIMPL, ("udf_vget called\n"));
+       return EOPNOTSUPP;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Lookup vnode for file handle specified
+ */
+int
+udf_fhtovp(struct mount *mp, struct fid *fhp,
+    struct vnode **vpp)
+{
+       DPRINTF(NOTIMPL, ("udf_fhtovp called\n"));
+       return EOPNOTSUPP;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Create an unique file handle. Its structure is opaque and won't be used by
+ * other subsystems. It should uniquely identify the file in the filingsystem
+ * and enough information to know if a file has been removed and/or resources
+ * have been recycled.
+ */
+int
+udf_vptofh(struct vnode *vp, struct fid *fid,
+    size_t *fh_size)
+{
+       DPRINTF(NOTIMPL, ("udf_vptofh called\n"));
+       return EOPNOTSUPP;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Create a filingsystem snapshot at the specified timestamp. Could be
+ * implemented by explicitly creating a new session or with spare room in the
+ * integrity descriptor space
+ */
+int
+udf_snapshot(struct mount *mp, struct vnode *vp,
+    struct timespec *tm)
+{
+       DPRINTF(NOTIMPL, ("udf_snapshot called\n"));
+       return EOPNOTSUPP;
+}
+
+/* --------------------------------------------------------------------- */
diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c
new file mode 100644 (file)
index 0000000..697d891
--- /dev/null
@@ -0,0 +1,2183 @@
+/* $NetBSD: udf_vnops.c,v 1.87 2013/10/18 19:56:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ * Generic parts are derived from software contributed to The NetBSD Foundation
+ * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
+ * 2005 program.
+ *
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__KERNEL_RCSID(0, "$NetBSD: udf_vnops.c,v 1.87 2013/10/18 19:56:55 christos Exp $");
+#endif /* not lint */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h>   /* defines plimit structure in proc struct */
+#include <sys/kernel.h>
+#include <sys/file.h>          /* define FWRITE ... */
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/lockf.h>
+#include <sys/kauth.h>
+
+#include <miscfs/genfs/genfs.h>
+#include <uvm/uvm_extern.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+#include <sys/dirhash.h>
+
+#include "udf.h"
+#include "udf_subr.h"
+#include "udf_bswap.h"
+
+
+#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data)
+
+
+/* externs */
+extern int prtactive;
+
+/* implementations of vnode functions; table follows at end */
+/* --------------------------------------------------------------------- */
+
+int
+udf_inactive(void *v)
+{
+       struct vop_inactive_args /* {
+               struct vnode *a_vp;
+               bool         *a_recycle;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct udf_node *udf_node = VTOI(vp);
+       int refcnt;
+
+       DPRINTF(NODE, ("udf_inactive called for udf_node %p\n", VTOI(vp)));
+
+       if (udf_node == NULL) {
+               DPRINTF(NODE, ("udf_inactive: inactive NULL UDF node\n"));
+               VOP_UNLOCK(vp);
+               return 0;
+       }
+
+       /*
+        * Optionally flush metadata to disc. If the file has not been
+        * referenced anymore in a directory we ought to free up the resources
+        * on disc if applicable.
+        */
+       if (udf_node->fe) {
+               refcnt = udf_rw16(udf_node->fe->link_cnt);
+       } else {
+               assert(udf_node->efe);
+               refcnt = udf_rw16(udf_node->efe->link_cnt);
+       }
+
+       if ((refcnt == 0) && (vp->v_vflag & VV_SYSTEM)) {
+               DPRINTF(VOLUMES, ("UDF_INACTIVE deleting VV_SYSTEM\n"));
+               /* system nodes are not writen out on inactive, so flush */
+               udf_node->i_flags = 0;
+       }
+
+       *ap->a_recycle = false;
+       if ((refcnt == 0) && ((vp->v_vflag & VV_SYSTEM) == 0)) {
+               /* remove this file's allocation */
+               DPRINTF(NODE, ("udf_inactive deleting unlinked file\n"));
+               *ap->a_recycle = true;
+               udf_delete_node(udf_node);
+               VOP_UNLOCK(vp);
+               return 0;
+       }
+
+       /* write out its node */
+       if (udf_node->i_flags & (IN_CHANGE | IN_UPDATE | IN_MODIFIED))
+               udf_update(vp, NULL, NULL, NULL, 0);
+       VOP_UNLOCK(vp);
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int udf_sync(struct mount *mp, int waitfor, kauth_cred_t cred, struct lwp *lwp);
+
+int
+udf_reclaim(void *v)
+{
+       struct vop_reclaim_args /* {
+               struct vnode *a_vp;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct udf_node *udf_node = VTOI(vp);
+
+       DPRINTF(NODE, ("udf_reclaim called for node %p\n", udf_node));
+       if (prtactive && vp->v_usecount > 1)
+               vprint("udf_reclaim(): pushing active", vp);
+
+       if (udf_node == NULL) {
+               DPRINTF(NODE, ("udf_reclaim(): null udfnode\n"));
+               return 0;
+       }
+
+       /* update note for closure */
+       udf_update(vp, NULL, NULL, NULL, UPDATE_CLOSE);
+
+       /* async check to see if all node descriptors are written out */
+       while ((volatile int) udf_node->outstanding_nodedscr > 0) {
+               vprint("udf_reclaim(): waiting for writeout\n", vp);
+               tsleep(&udf_node->outstanding_nodedscr, PRIBIO, "recl wait", hz/8);
+       }
+
+       /* dispose all node knowledge */
+       udf_dispose_node(udf_node);
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_read(void *v)
+{
+       struct vop_read_args /* {
+               struct vnode *a_vp;
+               struct uio *a_uio;
+               int a_ioflag;
+               kauth_cred_t a_cred;
+       } */ *ap = v;
+       struct vnode *vp     = ap->a_vp;
+       struct uio   *uio    = ap->a_uio;
+       int           ioflag = ap->a_ioflag;
+       int           advice = IO_ADV_DECODE(ap->a_ioflag);
+       struct uvm_object    *uobj;
+       struct udf_node      *udf_node = VTOI(vp);
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       uint64_t file_size;
+       vsize_t len;
+       int error;
+
+       /*
+        * XXX reading from extended attributes not yet implemented. FreeBSD
+        * has it in mind to forward the IO_EXT read call to the
+        * VOP_READEXTATTR().
+        */
+
+       DPRINTF(READ, ("udf_read called\n"));
+
+       /* can this happen? some filingsystems have this check */
+       if (uio->uio_offset < 0)
+               return EINVAL;
+       if (uio->uio_resid == 0)
+               return 0;
+
+       /* protect against rogue programs reading raw directories and links */
+       if ((ioflag & IO_ALTSEMANTICS) == 0) {
+               if (vp->v_type == VDIR)
+                       return EISDIR;
+               /* all but regular files just give EINVAL */
+               if (vp->v_type != VREG)
+                       return EINVAL;
+       }
+
+       assert(udf_node);
+       assert(udf_node->fe || udf_node->efe);
+
+       /* get file/directory filesize */
+       if (udf_node->fe) {
+               fe = udf_node->fe;
+               file_size = udf_rw64(fe->inf_len);
+       } else {
+               assert(udf_node->efe);
+               efe = udf_node->efe;
+               file_size = udf_rw64(efe->inf_len);
+       }
+
+       /* read contents using buffercache */
+       uobj = &vp->v_uobj;
+       error = 0;
+       while (uio->uio_resid > 0) {
+               /* reached end? */
+               if (file_size <= uio->uio_offset)
+                       break;
+
+               /* maximise length to file extremity */
+               len = MIN(file_size - uio->uio_offset, uio->uio_resid);
+               if (len == 0)
+                       break;
+
+               /* ubc, here we come, prepare to trap */
+               error = ubc_uiomove(uobj, uio, len, advice,
+                   UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp));
+               if (error)
+                       break;
+       }
+
+       /* note access time unless not requested */
+       if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) {
+               udf_node->i_flags |= IN_ACCESS;
+               if ((ioflag & IO_SYNC) == IO_SYNC)
+                       error = udf_update(vp, NULL, NULL, NULL, UPDATE_WAIT);
+       }
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_write(void *v)
+{
+       struct vop_write_args /* {
+               struct vnode *a_vp;
+               struct uio *a_uio;
+               int a_ioflag;
+               kauth_cred_t a_cred;
+       } */ *ap = v;
+       struct vnode *vp     = ap->a_vp;
+       struct uio   *uio    = ap->a_uio;
+       int           ioflag = ap->a_ioflag;
+       kauth_cred_t  cred   = ap->a_cred;
+       int           advice = IO_ADV_DECODE(ap->a_ioflag);
+       struct uvm_object    *uobj;
+       struct udf_node      *udf_node = VTOI(vp);
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       uint64_t file_size, old_size, old_offset;
+       vsize_t len;
+       int aflag = ioflag & IO_SYNC ? B_SYNC : 0;
+       int error;
+       int resid, extended;
+
+       /*
+        * XXX writing to extended attributes not yet implemented. FreeBSD has
+        * it in mind to forward the IO_EXT read call to the
+        * VOP_READEXTATTR().
+        */
+
+       DPRINTF(WRITE, ("udf_write called\n"));
+
+       /* can this happen? some filingsystems have this check */
+       if (uio->uio_offset < 0)
+               return EINVAL;
+       if (uio->uio_resid == 0)
+               return 0;
+
+       /* protect against rogue programs writing raw directories or links */
+       if ((ioflag & IO_ALTSEMANTICS) == 0) {
+               if (vp->v_type == VDIR)
+                       return EISDIR;
+               /* all but regular files just give EINVAL for now */
+               if (vp->v_type != VREG)
+                       return EINVAL;
+       }
+
+       assert(udf_node);
+       assert(udf_node->fe || udf_node->efe);
+
+       /* get file/directory filesize */
+       if (udf_node->fe) {
+               fe = udf_node->fe;
+               file_size = udf_rw64(fe->inf_len);
+       } else {
+               assert(udf_node->efe);
+               efe = udf_node->efe;
+               file_size = udf_rw64(efe->inf_len);
+       }
+       old_size = file_size;
+
+       /* if explicitly asked to append, uio_offset can be wrong? */
+       if (ioflag & IO_APPEND)
+               uio->uio_offset = file_size;
+
+       extended = (uio->uio_offset + uio->uio_resid > file_size);
+       if (extended) {
+               DPRINTF(WRITE, ("extending file from %"PRIu64" to %"PRIu64"\n",
+                       file_size, uio->uio_offset + uio->uio_resid));
+               error = udf_grow_node(udf_node, uio->uio_offset + uio->uio_resid);
+               if (error)
+                       return error;
+               file_size = uio->uio_offset + uio->uio_resid;
+       }
+
+       /* write contents using buffercache */
+       uobj = &vp->v_uobj;
+       resid = uio->uio_resid;
+       error = 0;
+
+       uvm_vnp_setwritesize(vp, file_size);
+       old_offset = uio->uio_offset;
+       while (uio->uio_resid > 0) {
+               /* maximise length to file extremity */
+               len = MIN(file_size - uio->uio_offset, uio->uio_resid);
+               if (len == 0)
+                       break;
+
+               genfs_node_wrlock(vp);
+               error = GOP_ALLOC(vp, uio->uio_offset, len, aflag, cred);
+               genfs_node_unlock(vp);
+               if (error)
+                       break;
+
+               /* ubc, here we come, prepare to trap */
+               error = ubc_uiomove(uobj, uio, len, advice,
+                   UBC_WRITE | UBC_UNMAP_FLAG(vp));
+               if (error)
+                       break;
+
+               /*
+                * flush what we just wrote if necessary.
+                * XXXUBC simplistic async flushing.
+                *
+                * Directories are excluded since its file data that we want
+                * to purge.
+                */
+               if ((vp->v_type != VDIR) &&
+                 (old_offset >> 16 != uio->uio_offset >> 16)) {
+                       mutex_enter(vp->v_interlock);
+                       error = VOP_PUTPAGES(vp, (old_offset >> 16) << 16,
+                           (uio->uio_offset >> 16) << 16,
+                           PGO_CLEANIT | PGO_LAZY);
+                       old_offset = uio->uio_offset;
+               }
+       }
+       uvm_vnp_setsize(vp, file_size);
+
+       /* mark node changed and request update */
+       udf_node->i_flags |= IN_CHANGE | IN_UPDATE;
+       if (vp->v_mount->mnt_flag & MNT_RELATIME)
+               udf_node->i_flags |= IN_ACCESS;
+
+       /*
+        * XXX TODO FFS has code here to reset setuid & setgid when we're not
+        * the superuser as a precaution against tampering.
+        */
+
+       /* if we wrote a thing, note write action on vnode */
+       if (resid > uio->uio_resid)
+               VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
+
+       if (error) {
+               /* bring back file size to its former size */
+               /* take notice of its errors? */
+               (void) udf_chsize(vp, (u_quad_t) old_size, cred);
+
+               /* roll back uio */
+               uio->uio_offset -= resid - uio->uio_resid;
+               uio->uio_resid = resid;
+       } else {
+               /* if we write and we're synchronous, update node */
+               if ((resid > uio->uio_resid) && ((ioflag & IO_SYNC) == IO_SYNC))
+                       error = udf_update(vp, NULL, NULL, NULL, UPDATE_WAIT);
+       }
+
+       return error;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * `Special' bmap functionality that translates all incomming requests to
+ * translate to vop_strategy() calls with the same blocknumbers effectively
+ * not translating at all.
+ */
+
+int
+udf_trivial_bmap(void *v)
+{
+       struct vop_bmap_args /* {
+               struct vnode *a_vp;
+               daddr_t a_bn;
+               struct vnode **a_vpp;
+               daddr_t *a_bnp;
+               int *a_runp;
+       } */ *ap = v;
+       struct vnode  *vp  = ap->a_vp;  /* our node     */
+       struct vnode **vpp = ap->a_vpp; /* return node  */
+       daddr_t *bnp  = ap->a_bnp;      /* translated   */
+       daddr_t  bn   = ap->a_bn;       /* origional    */
+       int     *runp = ap->a_runp;
+       struct udf_node *udf_node = VTOI(vp);
+       uint32_t lb_size;
+
+       /* get logical block size */
+       lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
+
+       /* could return `-1' to indicate holes/zeros */
+       /* translate 1:1 */
+       *bnp = bn;
+
+       /* set the vnode to read the data from with strategy on itself */
+       if (vpp)
+               *vpp = vp;
+
+       /* set runlength of maximum block size */
+       if (runp)
+               *runp = MAXPHYS / lb_size;      /* or with -1 ? */
+
+       /* return success */
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_vfsstrategy(void *v)
+{
+       struct vop_strategy_args /* {
+               struct vnode *a_vp;
+               struct buf *a_bp;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct buf   *bp = ap->a_bp;
+       struct udf_node *udf_node = VTOI(vp);
+       uint32_t lb_size, sectors;
+
+       DPRINTF(STRATEGY, ("udf_strategy called\n"));
+
+       /* check if we ought to be here */
+       if (vp->v_type == VBLK || vp->v_type == VCHR)
+               panic("udf_strategy: spec");
+
+       /* only filebuffers ought to be read/write by this, no descriptors */
+       assert(bp->b_blkno >= 0);
+
+       /* get sector size */
+       lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
+
+       /* calculate length to fetch/store in sectors */
+       sectors = bp->b_bcount / lb_size;
+       assert(bp->b_bcount > 0);
+
+       /* NEVER assume later that this buffer is already translated */
+       /* bp->b_lblkno = bp->b_blkno; */
+
+       /* check assertions: we OUGHT to always get multiples of this */
+       assert(sectors * lb_size == bp->b_bcount);
+
+       /* issue buffer */
+       if (bp->b_flags & B_READ) {
+               DPRINTF(STRATEGY, ("\tread vp %p buf %p (blk no %"PRIu64")"
+                   ", for %d sectors\n",
+                   vp, bp, bp->b_blkno, sectors));
+
+               /* read buffer from the udf_node, translate vtop on the way*/
+               udf_read_filebuf(udf_node, bp);
+       } else {
+               DPRINTF(STRATEGY, ("\twrite vp %p buf %p (blk no %"PRIu64")"
+                   ", for %d sectors\n",
+                   vp, bp, bp->b_blkno, sectors));
+
+               /* write buffer to the udf_node, translate vtop on the way*/
+               udf_write_filebuf(udf_node, bp);
+       }
+
+       return bp->b_error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_readdir(void *v)
+{
+       struct vop_readdir_args /* {
+               struct vnode *a_vp;
+               struct uio *a_uio;
+               kauth_cred_t a_cred;
+               int *a_eofflag;
+               off_t **a_cookies;
+               int *a_ncookies;
+       } */ *ap = v;
+       struct uio *uio = ap->a_uio;
+       struct vnode *vp = ap->a_vp;
+       struct udf_node *udf_node = VTOI(vp);
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       struct fileid_desc *fid;
+       struct dirent *dirent;
+       uint64_t file_size, diroffset, transoffset;
+       uint32_t lb_size;
+       int error;
+
+       DPRINTF(READDIR, ("udf_readdir called\n"));
+
+       /* This operation only makes sense on directory nodes. */
+       if (vp->v_type != VDIR)
+               return ENOTDIR;
+
+       /* get directory filesize */
+       if (udf_node->fe) {
+               fe = udf_node->fe;
+               file_size = udf_rw64(fe->inf_len);
+       } else {
+               assert(udf_node->efe);
+               efe = udf_node->efe;
+               file_size = udf_rw64(efe->inf_len);
+       }
+
+       dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK | M_ZERO);
+
+       /*
+        * Add `.' pseudo entry if at offset zero since its not in the fid
+        * stream
+        */
+       if (uio->uio_offset == 0) {
+               DPRINTF(READDIR, ("\t'.' inserted\n"));
+               strcpy(dirent->d_name, ".");
+               dirent->d_fileno = udf_get_node_id(&udf_node->loc);
+               dirent->d_type = DT_DIR;
+               dirent->d_namlen = strlen(dirent->d_name);
+               dirent->d_reclen = _DIRENT_SIZE(dirent);
+               uiomove(dirent, _DIRENT_SIZE(dirent), uio);
+
+               /* mark with magic value that we have done the dummy */
+               uio->uio_offset = UDF_DIRCOOKIE_DOT;
+       }
+
+       /* we are called just as long as we keep on pushing data in */
+       error = 0;
+       if (uio->uio_offset < file_size) {
+               /* allocate temporary space for fid */
+               lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
+               fid = malloc(lb_size, M_UDFTEMP, M_WAITOK);
+
+               if (uio->uio_offset == UDF_DIRCOOKIE_DOT)
+                       uio->uio_offset = 0;
+
+               diroffset   = uio->uio_offset;
+               transoffset = diroffset;
+               while (diroffset < file_size) {
+                       DPRINTF(READDIR, ("\tread in fid stream\n"));
+                       /* transfer a new fid/dirent */
+                       error = udf_read_fid_stream(vp, &diroffset, fid, dirent);
+                       DPRINTFIF(READDIR, error, ("read error in read fid "
+                           "stream : %d\n", error));
+                       if (error)
+                               break;
+
+                       /* 
+                        * If there isn't enough space in the uio to return a
+                        * whole dirent, break off read
+                        */
+                       if (uio->uio_resid < _DIRENT_SIZE(dirent))
+                               break;
+
+                       /* remember the last entry we transfered */
+                       transoffset = diroffset;
+
+                       /* skip deleted entries */
+                       if (fid->file_char & UDF_FILE_CHAR_DEL)
+                               continue;
+
+                       /* skip not visible files */
+                       if (fid->file_char & UDF_FILE_CHAR_VIS)
+                               continue;
+
+                       /* copy dirent to the caller */
+                       DPRINTF(READDIR, ("\tread dirent `%s', type %d\n",
+                           dirent->d_name, dirent->d_type));
+                       uiomove(dirent, _DIRENT_SIZE(dirent), uio);
+               }
+
+               /* pass on last transfered offset */
+               uio->uio_offset = transoffset;
+               free(fid, M_UDFTEMP);
+       }
+
+       if (ap->a_eofflag)
+               *ap->a_eofflag = (uio->uio_offset >= file_size);
+
+#ifdef DEBUG
+       if (udf_verbose & UDF_DEBUG_READDIR) {
+               printf("returning offset %d\n", (uint32_t) uio->uio_offset);
+               if (ap->a_eofflag)
+                       printf("returning EOF ? %d\n", *ap->a_eofflag);
+               if (error)
+                       printf("readdir returning error %d\n", error);
+       }
+#endif
+
+       free(dirent, M_UDFTEMP);
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_lookup(void *v)
+{
+       struct vop_lookup_args /* {
+               struct vnode *a_dvp;
+               struct vnode **a_vpp;
+               struct componentname *a_cnp;
+       } */ *ap = v;
+       struct vnode *dvp = ap->a_dvp;
+       struct vnode **vpp = ap->a_vpp;
+       struct componentname *cnp = ap->a_cnp;
+       struct udf_node  *dir_node, *res_node;
+       struct udf_mount *ump;
+       struct long_ad    icb_loc;
+       mode_t mode;
+       uid_t d_uid;
+       gid_t d_gid;
+       const char *name;
+       int namelen, nameiop, islastcn, mounted_ro;
+       int error, found;
+
+       dir_node = VTOI(dvp);
+       ump = dir_node->ump;
+       *vpp = NULL;
+
+       DPRINTF(LOOKUP, ("udf_lookup called, lookup `%s`\n",
+               cnp->cn_nameptr));
+
+       /* simplify/clarification flags */
+       nameiop     = cnp->cn_nameiop;
+       islastcn    = cnp->cn_flags & ISLASTCN;
+       mounted_ro  = dvp->v_mount->mnt_flag & MNT_RDONLY;
+
+       /* check exec/dirread permissions first */
+       error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred);
+       if (error)
+               return error;
+
+       DPRINTF(LOOKUP, ("\taccess ok\n"));
+
+       /*
+        * If requesting a modify on the last path element on a read-only
+        * filingsystem, reject lookup; XXX why is this repeated in every FS ?
+        */
+       if (islastcn && mounted_ro && (nameiop == DELETE || nameiop == RENAME))
+               return EROFS;
+
+       DPRINTF(LOOKUP, ("\tlooking up cnp->cn_nameptr '%s'\n",
+           cnp->cn_nameptr));
+       /* look in the namecache */
+       if (cache_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
+                        cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) {
+               return *vpp == NULLVP ? ENOENT : 0;
+       }
+
+       DPRINTF(LOOKUP, ("\tNOT found in cache\n"));
+
+       /*
+        * Obviously, the file is not (anymore) in the namecache, we have to
+        * search for it. There are three basic cases: '.', '..' and others.
+        * 
+        * Following the guidelines of VOP_LOOKUP manpage and tmpfs.
+        */
+       error = 0;
+       if ((cnp->cn_namelen == 1) && (cnp->cn_nameptr[0] == '.')) {
+               DPRINTF(LOOKUP, ("\tlookup '.'\n"));
+               /* special case 1 '.' */
+               if (islastcn && cnp->cn_nameiop == RENAME) {
+                       error = EISDIR;
+                       goto out;
+               }
+               vref(dvp);
+               *vpp = dvp;
+               /* done */
+               goto done;
+       } else if (cnp->cn_flags & ISDOTDOT) {
+               /* special case 2 '..' */
+               DPRINTF(LOOKUP, ("\tlookup '..'\n"));
+
+               if (islastcn && cnp->cn_nameiop == RENAME) {
+                       error = EINVAL;
+                       goto out;
+               }
+
+               /* get our node */
+               name    = "..";
+               namelen = 2;
+               error = udf_lookup_name_in_dir(dvp, name, namelen,
+                               &icb_loc, &found);
+               if (error)
+                       goto out;
+               if (!found)
+                       error = ENOENT;
+
+               /* first unlock parent */
+               VOP_UNLOCK(dvp);
+
+               if (error == 0) {
+                       DPRINTF(LOOKUP, ("\tfound '..'\n"));
+                       /* try to create/reuse the node */
+                       error = udf_get_node(ump, &icb_loc, &res_node);
+
+                       if (!error) {
+                               DPRINTF(LOOKUP,
+                                       ("\tnode retrieved/created OK\n"));
+                               *vpp = res_node->vnode;
+                       }
+               }
+
+               /* try to relock parent */
+               vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+               goto out;
+       }
+
+       /* all other files */
+       DPRINTF(LOOKUP, ("\tlookup file/dir in directory\n"));
+
+       /* lookup filename in the directory; location icb_loc */
+       name    = cnp->cn_nameptr;
+       namelen = cnp->cn_namelen;
+       error = udf_lookup_name_in_dir(dvp, name, namelen,
+                       &icb_loc, &found);
+       if (error)
+               goto out;
+       if (!found) {
+               DPRINTF(LOOKUP, ("\tNOT found\n"));
+               /*
+                * The entry was not found in the directory.  This is
+                * valid if we are creating or renaming an entry and
+                * are working on the last component of the path name.
+                */
+               if (islastcn && (cnp->cn_nameiop == CREATE ||
+                                cnp->cn_nameiop == RENAME)) {
+                       error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
+                       if (error) {
+                               goto out;
+                       }
+                       error = EJUSTRETURN;
+               } else {
+                       error = ENOENT;
+               }
+               /* done */
+               goto done;
+       }
+
+       /*
+        * XXX NOTE tmpfs has a test here that tests that intermediate
+        * components i.e. not the last one ought to be either a directory or
+        * a link. It seems to function well without this code.
+        */
+
+       /* try to create/reuse the node */
+       error = udf_get_node(ump, &icb_loc, &res_node);
+       if (error)
+               goto out;
+
+       /* check permissions */
+       if (islastcn && (cnp->cn_nameiop == DELETE ||
+                        cnp->cn_nameiop == RENAME)  ) {
+               error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
+               if (error) {
+                       vput(res_node->vnode);
+                       goto out;
+               }
+
+               /*
+                * Check if the directory has its sticky bit set. If so, ask
+                * for clearance since only the owner of a file or directory
+                * can remove/rename from taht directory.
+                */
+               mode = udf_getaccessmode(dir_node);
+               if ((mode & S_ISTXT) != 0) {
+                       udf_getownership(dir_node, &d_uid, &d_gid);
+                       error = kauth_authorize_vnode(cnp->cn_cred,
+                           KAUTH_VNODE_DELETE, res_node->vnode,
+                           dir_node->vnode, genfs_can_sticky(cnp->cn_cred,
+                           d_uid, d_uid));
+                       if (error) {
+                               error = EPERM;
+                               vput(res_node->vnode);
+                               goto out;
+                       }
+               }
+       }
+
+       *vpp = res_node->vnode;
+
+done:
+       /*
+        * Store result in the cache if requested. If we are creating a file,
+        * the file might not be found and thus putting it into the namecache
+        * might be seen as negative caching.
+        */
+       if (nameiop != CREATE)
+               cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen,
+                           cnp->cn_flags);
+
+out:
+       DPRINTFIF(LOOKUP, error, ("udf_lookup returing error %d\n", error));
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_getattr(void *v)
+{
+       struct vop_getattr_args /* {
+               struct vnode *a_vp;
+               struct vattr *a_vap;
+               kauth_cred_t a_cred;
+               struct lwp   *a_l;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct udf_node *udf_node = VTOI(vp);
+       struct udf_mount *ump = udf_node->ump;
+       struct file_entry    *fe  = udf_node->fe;
+       struct extfile_entry *efe = udf_node->efe;
+       struct filetimes_extattr_entry *ft_extattr;
+       struct device_extattr_entry *devattr;
+       struct vattr *vap = ap->a_vap;
+       struct timestamp *atime, *mtime, *attrtime, *creatime;
+       uint64_t filesize, blkssize;
+       uint32_t nlink;
+       uint32_t offset, a_l;
+       uint8_t *filedata;
+       uid_t uid;
+       gid_t gid;
+       int error;
+
+       DPRINTF(CALL, ("udf_getattr called\n"));
+
+       /* update times before we returning values */ 
+       udf_itimes(udf_node, NULL, NULL, NULL);
+
+       /* get descriptor information */
+       if (fe) {
+               nlink    = udf_rw16(fe->link_cnt);
+               uid      = (uid_t)udf_rw32(fe->uid);
+               gid      = (gid_t)udf_rw32(fe->gid);
+               filesize = udf_rw64(fe->inf_len);
+               blkssize = udf_rw64(fe->logblks_rec);
+               atime    = &fe->atime;
+               mtime    = &fe->mtime;
+               attrtime = &fe->attrtime;
+               filedata = fe->data;
+
+               /* initial guess */
+               creatime = mtime;
+
+               /* check our extended attribute if present */
+               error = udf_extattr_search_intern(udf_node,
+                       UDF_FILETIMES_ATTR_NO, "", &offset, &a_l);
+               if (!error) {
+                       ft_extattr = (struct filetimes_extattr_entry *)
+                               (filedata + offset);
+                       if (ft_extattr->existence & UDF_FILETIMES_FILE_CREATION)
+                               creatime = &ft_extattr->times[0];
+               }
+       } else {
+               assert(udf_node->efe);
+               nlink    = udf_rw16(efe->link_cnt);
+               uid      = (uid_t)udf_rw32(efe->uid);
+               gid      = (gid_t)udf_rw32(efe->gid);
+               filesize = udf_rw64(efe->inf_len);      /* XXX or obj_size? */
+               blkssize = udf_rw64(efe->logblks_rec);
+               atime    = &efe->atime;
+               mtime    = &efe->mtime;
+               attrtime = &efe->attrtime;
+               creatime = &efe->ctime;
+               filedata = efe->data;
+       }
+
+       /* do the uid/gid translation game */
+       if (uid == (uid_t) -1)
+               uid = ump->mount_args.anon_uid;
+       if (gid == (gid_t) -1)
+               gid = ump->mount_args.anon_gid;
+
+       /* fill in struct vattr with values from the node */
+       vattr_null(vap);
+       vap->va_type      = vp->v_type;
+       vap->va_mode      = udf_getaccessmode(udf_node);
+       vap->va_nlink     = nlink;
+       vap->va_uid       = uid;
+       vap->va_gid       = gid;
+       vap->va_fsid      = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
+       vap->va_fileid    = udf_get_node_id(&udf_node->loc);   /* inode hash XXX */
+       vap->va_size      = filesize;
+       vap->va_blocksize = udf_node->ump->discinfo.sector_size;  /* wise? */
+
+       /*
+        * BUG-ALERT: UDF doesn't count '.' as an entry, so we'll have to add
+        * 1 to the link count if its a directory we're requested attributes
+        * of.
+        */
+       if (vap->va_type == VDIR)
+               vap->va_nlink++;
+
+       /* access times */
+       udf_timestamp_to_timespec(ump, atime,    &vap->va_atime);
+       udf_timestamp_to_timespec(ump, mtime,    &vap->va_mtime);
+       udf_timestamp_to_timespec(ump, attrtime, &vap->va_ctime);
+       udf_timestamp_to_timespec(ump, creatime, &vap->va_birthtime);
+
+       vap->va_gen       = 1;          /* no multiple generations yes (!?) */
+       vap->va_flags     = 0;          /* no flags */
+       vap->va_bytes     = blkssize * udf_node->ump->discinfo.sector_size;
+       vap->va_filerev   = 1;          /* TODO file revision numbers? */
+       vap->va_vaflags   = 0;
+       /* TODO get vaflags from the extended attributes? */
+
+       if ((vap->va_type == VBLK) || (vap->va_type == VCHR)) {
+               error = udf_extattr_search_intern(udf_node,
+                               UDF_DEVICESPEC_ATTR_NO, "",
+                               &offset, &a_l);
+               /* if error, deny access */
+               if (error || (filedata == NULL)) {
+                       vap->va_mode = 0;       /* or v_type = VNON?  */
+               } else {
+                       devattr = (struct device_extattr_entry *)
+                               filedata + offset;
+                       vap->va_rdev = makedev(
+                               udf_rw32(devattr->major),
+                               udf_rw32(devattr->minor)
+                               );
+                       /* TODO we could check the implementator */
+               }
+       }
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_chown(struct vnode *vp, uid_t new_uid, gid_t new_gid,
+         kauth_cred_t cred)
+{
+       struct udf_node  *udf_node = VTOI(vp);
+       uid_t uid;
+       gid_t gid;
+       int error;
+
+#ifdef notyet
+       /* TODO get vaflags from the extended attributes? */
+       /* Immutable or append-only files cannot be modified, either. */
+       if (udf_node->flags & (IMMUTABLE | APPEND))
+               return EPERM;
+#endif
+
+       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+               return EROFS;
+
+       /* retrieve old values */
+       udf_getownership(udf_node, &uid, &gid);
+
+       /* only one could be specified */
+       if (new_uid == VNOVAL)
+               new_uid = uid;
+       if (new_gid == VNOVAL)
+               new_gid = gid;
+
+       /* check if we can fit it in an 32 bits */
+       if ((uid_t) ((uint32_t) new_uid) != new_uid)
+               return EINVAL;
+       if ((gid_t) ((uint32_t) new_gid) != new_gid)
+               return EINVAL;
+
+       /* check permissions */
+       error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP,
+           vp, NULL, genfs_can_chown(cred, uid, gid, new_uid, new_gid));
+       if (error)
+               return (error);
+
+       /* change the ownership */
+       udf_setownership(udf_node, new_uid, new_gid);
+
+       /* mark node changed */
+       udf_node->i_flags |= IN_CHANGE;
+       if (vp->v_mount->mnt_flag & MNT_RELATIME)
+               udf_node->i_flags |= IN_ACCESS;
+
+       return 0;
+}
+
+
+static int
+udf_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred)
+{
+       struct udf_node  *udf_node = VTOI(vp);
+       uid_t uid;
+       gid_t gid;
+       int error;
+
+#ifdef notyet
+       /* TODO get vaflags from the extended attributes? */
+       /* Immutable or append-only files cannot be modified, either. */
+       if (udf_node->flags & (IMMUTABLE | APPEND))
+               return EPERM;
+#endif
+
+       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+               return EROFS;
+
+       /* retrieve uid/gid values */
+       udf_getownership(udf_node, &uid, &gid);
+
+       /* check permissions */
+       error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp,
+           NULL, genfs_can_chmod(vp->v_type, cred, uid, gid, mode));
+       if (error)
+               return (error);
+
+       /* change mode */
+       udf_setaccessmode(udf_node, mode);
+
+       /* mark node changed */
+       udf_node->i_flags |= IN_CHANGE;
+       if (vp->v_mount->mnt_flag & MNT_RELATIME)
+               udf_node->i_flags |= IN_ACCESS;
+
+       return 0;
+}
+
+
+/* exported */
+int
+udf_chsize(struct vnode *vp, u_quad_t newsize, kauth_cred_t cred)
+{
+       struct udf_node  *udf_node = VTOI(vp);
+       int error, extended;
+
+       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+               return EROFS;
+
+       /* Decide whether this is a valid operation based on the file type. */
+       switch (vp->v_type) {
+       case VDIR:
+               return EISDIR;
+       case VREG:
+               if (vp->v_mount->mnt_flag & MNT_RDONLY)
+                       return EROFS;
+               break;
+       case VBLK:
+               /* FALLTHROUGH */
+       case VCHR:
+               /* FALLTHROUGH */
+       case VFIFO:
+               /* Allow modifications of special files even if in the file
+                * system is mounted read-only (we are not modifying the
+                * files themselves, but the objects they represent). */
+               return 0;
+       default:
+               /* Anything else is unsupported. */
+               return EOPNOTSUPP;
+       }
+
+#if notyet
+       /* TODO get vaflags from the extended attributes? */
+       /* Immutable or append-only files cannot be modified, either. */
+       if (node->flags & (IMMUTABLE | APPEND))
+               return EPERM;
+#endif
+
+       /* resize file to the requested size */
+       error = udf_resize_node(udf_node, newsize, &extended);
+
+       if (error == 0) {
+               /* mark change */
+               udf_node->i_flags |= IN_CHANGE | IN_MODIFY;
+               if (vp->v_mount->mnt_flag & MNT_RELATIME)
+                       udf_node->i_flags |= IN_ACCESS;
+               VN_KNOTE(vp, NOTE_ATTRIB | (extended ? NOTE_EXTEND : 0));
+               udf_update(vp, NULL, NULL, NULL, 0);
+       }
+
+       return error;
+}
+
+
+static int
+udf_chflags(struct vnode *vp, mode_t mode, kauth_cred_t cred)
+{
+       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+               return EROFS;
+
+       /*
+        * XXX we can't do this yet, as its not described in the standard yet
+        */
+
+       return EOPNOTSUPP;
+}
+
+
+static int
+udf_chtimes(struct vnode *vp,
+       struct timespec *atime, struct timespec *mtime,
+       struct timespec *birthtime, int setattrflags,
+       kauth_cred_t cred)
+{
+       struct udf_node  *udf_node = VTOI(vp);
+       uid_t uid;
+       gid_t gid;
+       int error;
+
+#ifdef notyet
+       /* TODO get vaflags from the extended attributes? */
+       /* Immutable or append-only files cannot be modified, either. */
+       if (udf_node->flags & (IMMUTABLE | APPEND))
+               return EPERM;
+#endif
+
+       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+               return EROFS;
+
+       /* retrieve uid/gid values */
+       udf_getownership(udf_node, &uid, &gid);
+
+       /* check permissions */
+       error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp,
+           NULL, genfs_can_chtimes(vp, setattrflags, uid, cred));
+       if (error)
+               return (error);
+
+       /* update node flags depending on what times are passed */
+       if (atime->tv_sec != VNOVAL)
+               if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
+                       udf_node->i_flags |= IN_ACCESS;
+       if ((mtime->tv_sec != VNOVAL) || (birthtime->tv_sec != VNOVAL)) {
+               udf_node->i_flags |= IN_CHANGE | IN_UPDATE;
+               if (vp->v_mount->mnt_flag & MNT_RELATIME)
+                       udf_node->i_flags |= IN_ACCESS;
+       }
+
+       return udf_update(vp, atime, mtime, birthtime, 0);
+}
+
+
+int
+udf_setattr(void *v)
+{
+       struct vop_setattr_args /* {
+               struct vnode *a_vp;
+               struct vattr *a_vap;
+               kauth_cred_t a_cred;
+               struct lwp   *a_l;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+/*     struct udf_node  *udf_node = VTOI(vp); */
+/*     struct udf_mount *ump = udf_node->ump; */
+       kauth_cred_t cred = ap->a_cred;
+       struct vattr *vap = ap->a_vap;
+       int error;
+
+       DPRINTF(CALL, ("udf_setattr called\n"));
+
+       /* Abort if any unsettable attribute is given. */
+       error = 0;
+       if (vap->va_type != VNON ||
+           vap->va_nlink != VNOVAL ||
+           vap->va_fsid != VNOVAL ||
+           vap->va_fileid != VNOVAL ||
+           vap->va_blocksize != VNOVAL ||
+#ifdef notyet
+           /* checks are debated */
+           vap->va_ctime.tv_sec != VNOVAL ||
+           vap->va_ctime.tv_nsec != VNOVAL ||
+           vap->va_birthtime.tv_sec != VNOVAL ||
+           vap->va_birthtime.tv_nsec != VNOVAL ||
+#endif
+           vap->va_gen != VNOVAL ||
+           vap->va_rdev != VNOVAL ||
+           vap->va_bytes != VNOVAL)
+               error = EINVAL;
+
+       DPRINTF(ATTR, ("setattr changing:\n"));
+       if (error == 0 && (vap->va_flags != VNOVAL)) {
+               DPRINTF(ATTR, ("\tchflags\n"));
+               error = udf_chflags(vp, vap->va_flags, cred);
+       }
+
+       if (error == 0 && (vap->va_size != VNOVAL)) {
+               DPRINTF(ATTR, ("\tchsize\n"));
+               error = udf_chsize(vp, vap->va_size, cred);
+       }
+
+       if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) {
+               DPRINTF(ATTR, ("\tchown\n"));
+               error = udf_chown(vp, vap->va_uid, vap->va_gid, cred);
+       }
+
+       if (error == 0 && (vap->va_mode != VNOVAL)) {
+               DPRINTF(ATTR, ("\tchmod\n"));
+               error = udf_chmod(vp, vap->va_mode, cred);
+       }
+
+       if (error == 0 &&
+           ((vap->va_atime.tv_sec != VNOVAL &&
+             vap->va_atime.tv_nsec != VNOVAL)   ||
+            (vap->va_mtime.tv_sec != VNOVAL &&
+             vap->va_mtime.tv_nsec != VNOVAL))
+           ) {
+               DPRINTF(ATTR, ("\tchtimes\n"));
+               error = udf_chtimes(vp, &vap->va_atime, &vap->va_mtime,
+                   &vap->va_birthtime, vap->va_vaflags, cred);
+       }
+       VN_KNOTE(vp, NOTE_ATTRIB);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Return POSIX pathconf information for UDF file systems.
+ */
+int
+udf_pathconf(void *v)
+{
+       struct vop_pathconf_args /* {
+               struct vnode *a_vp;
+               int a_name;
+               register_t *a_retval;
+       } */ *ap = v;
+       uint32_t bits;
+
+       DPRINTF(CALL, ("udf_pathconf called\n"));
+
+       switch (ap->a_name) {
+       case _PC_LINK_MAX:
+               *ap->a_retval = (1<<16)-1;      /* 16 bits */
+               return 0;
+       case _PC_NAME_MAX:
+               *ap->a_retval = UDF_MAXNAMLEN;
+               return 0;
+       case _PC_PATH_MAX:
+               *ap->a_retval = PATH_MAX;
+               return 0;
+       case _PC_PIPE_BUF:
+               *ap->a_retval = PIPE_BUF;
+               return 0;
+       case _PC_CHOWN_RESTRICTED:
+               *ap->a_retval = 1;
+               return 0;
+       case _PC_NO_TRUNC:
+               *ap->a_retval = 1;
+               return 0;
+       case _PC_SYNC_IO:
+               *ap->a_retval = 0;     /* synchronised is off for performance */
+               return 0;
+       case _PC_FILESIZEBITS:
+               /* 64 bit file offsets -> 2+floor(2log(2^64-1)) = 2 + 63 = 65 */
+               bits = 64; /* XXX ought to deliver 65 */
+#if 0
+               if (udf_node)
+                       bits = 64 * vp->v_mount->mnt_dev_bshift;
+#endif
+               *ap->a_retval = bits;
+               return 0;
+       }
+
+       return EINVAL;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_open(void *v)
+{
+       struct vop_open_args /* {
+               struct vnode *a_vp;
+               int a_mode;
+               kauth_cred_t a_cred;
+               struct proc *a_p;
+       } */ *ap = v;
+       int flags;
+
+       DPRINTF(CALL, ("udf_open called\n"));
+
+       /*
+        * Files marked append-only must be opened for appending.
+        * TODO: get chflags(2) flags from extened attribute.
+        */
+       flags = 0;
+       if ((flags & APPEND) && (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
+               return (EPERM);
+
+       return 0;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_close(void *v)
+{
+       struct vop_close_args /* {
+               struct vnode *a_vp;
+               int a_fflag;
+               kauth_cred_t a_cred;
+               struct proc *a_p;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct udf_node *udf_node = VTOI(vp);
+       int async = vp->v_mount->mnt_flag & MNT_ASYNC;
+       int error;
+
+       DPRINTF(CALL, ("udf_close called\n"));
+       udf_node = udf_node;    /* shut up gcc */
+
+       if (!async && (vp->v_type != VDIR)) {
+               mutex_enter(vp->v_interlock);
+               error = VOP_PUTPAGES(vp, 0, 0, PGO_CLEANIT);
+               if (error)
+                       return error;
+       }
+
+       mutex_enter(vp->v_interlock);
+               if (vp->v_usecount > 1)
+                       udf_itimes(udf_node, NULL, NULL, NULL);
+       mutex_exit(vp->v_interlock);
+
+       return 0;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_check_possible(struct vnode *vp, struct vattr *vap, mode_t mode)
+{
+       int flags;
+
+       /* check if we are allowed to write */
+       switch (vap->va_type) {
+       case VDIR:
+       case VLNK:
+       case VREG:
+               /*
+                * normal nodes: check if we're on a read-only mounted
+                * filingsystem and bomb out if we're trying to write.
+                */
+               if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY))
+                       return EROFS;
+               break;
+       case VBLK:
+       case VCHR:
+       case VSOCK:
+       case VFIFO:
+               /*
+                * special nodes: even on read-only mounted filingsystems
+                * these are allowed to be written to if permissions allow.
+                */
+               break;
+       default:
+               /* no idea what this is */
+               return EINVAL;
+       }
+
+       /* noone may write immutable files */
+       /* TODO: get chflags(2) flags from extened attribute. */
+       flags = 0;
+       if ((mode & VWRITE) && (flags & IMMUTABLE))
+               return EPERM;
+
+       return 0;
+}
+
+static int
+udf_check_permitted(struct vnode *vp, struct vattr *vap, mode_t mode,
+    kauth_cred_t cred)
+{
+       /* ask the generic genfs_can_access to advice on security */
+       return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode,
+           vp->v_type, vap->va_mode), vp, NULL, genfs_can_access(vp->v_type,
+           vap->va_mode, vap->va_uid, vap->va_gid, mode, cred));
+}
+
+int
+udf_access(void *v)
+{
+       struct vop_access_args /* {
+               struct vnode *a_vp;
+               int a_mode;
+               kauth_cred_t a_cred;
+               struct proc *a_p;
+       } */ *ap = v;
+       struct vnode    *vp   = ap->a_vp;
+       mode_t           mode = ap->a_mode;
+       kauth_cred_t     cred = ap->a_cred;
+       /* struct udf_node *udf_node = VTOI(vp); */
+       struct vattr vap;
+       int error;
+
+       DPRINTF(CALL, ("udf_access called\n"));
+
+       error = VOP_GETATTR(vp, &vap, NULL);
+       if (error)
+               return error;
+
+       error = udf_check_possible(vp, &vap, mode);
+       if (error)
+               return error;
+
+       error = udf_check_permitted(vp, &vap, mode, cred);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_create(void *v)
+{
+       struct vop_create_args /* {
+               struct vnode *a_dvp;
+               struct vnode **a_vpp;
+               struct componentname *a_cnp;
+               struct vattr *a_vap;
+       } */ *ap = v;
+       struct vnode  *dvp = ap->a_dvp;
+       struct vnode **vpp = ap->a_vpp;
+       struct vattr  *vap  = ap->a_vap;
+       struct componentname *cnp = ap->a_cnp;
+       int error;
+
+       DPRINTF(CALL, ("udf_create called\n"));
+       error = udf_create_node(dvp, vpp, vap, cnp);
+
+       vput(dvp);
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_mknod(void *v)
+{
+       struct vop_mknod_args /* {
+               struct vnode *a_dvp;
+               struct vnode **a_vpp;
+               struct componentname *a_cnp;
+               struct vattr *a_vap;
+       } */ *ap = v;
+       struct vnode  *dvp = ap->a_dvp;
+       struct vnode **vpp = ap->a_vpp;
+       struct vattr  *vap  = ap->a_vap;
+       struct componentname *cnp = ap->a_cnp;
+       int error;
+
+       DPRINTF(CALL, ("udf_mknod called\n"));
+       error = udf_create_node(dvp, vpp, vap, cnp);
+
+       vput(dvp);
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_mkdir(void *v)
+{
+       struct vop_mkdir_args /* {
+               struct vnode *a_dvp;
+               struct vnode **a_vpp;
+               struct componentname *a_cnp;
+               struct vattr *a_vap;
+       } */ *ap = v;
+       struct vnode  *dvp = ap->a_dvp;
+       struct vnode **vpp = ap->a_vpp;
+       struct vattr  *vap  = ap->a_vap;
+       struct componentname *cnp = ap->a_cnp;
+       int error;
+
+       DPRINTF(CALL, ("udf_mkdir called\n"));
+       error = udf_create_node(dvp, vpp, vap, cnp);
+
+       vput(dvp);
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_do_link(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
+{
+       struct udf_node *udf_node, *dir_node;
+       struct vattr vap;
+       int error;
+
+       DPRINTF(CALL, ("udf_link called\n"));
+       KASSERT(dvp != vp);
+       KASSERT(vp->v_type != VDIR);
+       KASSERT(dvp->v_mount == vp->v_mount);
+
+       /* get attributes */
+       dir_node = VTOI(dvp);
+       udf_node = VTOI(vp);
+
+       error = VOP_GETATTR(vp, &vap, FSCRED);
+       if (error) {
+               VOP_UNLOCK(vp);
+               return error;
+       }
+
+       /* check link count overflow */
+       if (vap.va_nlink >= (1<<16)-1) {        /* uint16_t */
+               VOP_UNLOCK(vp);
+               return EMLINK;
+       }
+
+       error = udf_dir_attach(dir_node->ump, dir_node, udf_node, &vap, cnp);
+       if (error)
+               VOP_UNLOCK(vp);
+       return error;
+}
+
+int
+udf_link(void *v)
+{
+       struct vop_link_args /* {
+               struct vnode *a_dvp;
+               struct vnode *a_vp;
+               struct componentname *a_cnp;
+       } */ *ap = v;
+       struct vnode *dvp = ap->a_dvp;
+       struct vnode *vp  = ap->a_vp;
+       struct componentname *cnp = ap->a_cnp;
+       int error;
+
+       error = udf_do_link(dvp, vp, cnp);
+       if (error)
+               VOP_ABORTOP(dvp, cnp);
+
+       VN_KNOTE(vp, NOTE_LINK);
+       VN_KNOTE(dvp, NOTE_WRITE);
+       vput(dvp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_do_symlink(struct udf_node *udf_node, char *target)
+{
+       struct pathcomp pathcomp;
+       uint8_t *pathbuf, *pathpos, *compnamepos;
+       char *mntonname;
+       int pathlen, len, compnamelen, mntonnamelen;
+       int error;
+
+       /* process `target' to an UDF structure */
+       pathbuf = malloc(UDF_SYMLINKBUFLEN, M_UDFTEMP, M_WAITOK);
+       pathpos = pathbuf;
+       pathlen = 0;
+
+       if (*target == '/') {
+               /* symlink starts from the root */
+               len = UDF_PATH_COMP_SIZE;
+               memset(&pathcomp, 0, len);
+               pathcomp.type = UDF_PATH_COMP_ROOT;
+
+               /* check if its mount-point relative! */
+               mntonname    = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname;
+               mntonnamelen = strlen(mntonname);
+               if (strlen(target) >= mntonnamelen) {
+                       if (strncmp(target, mntonname, mntonnamelen) == 0) {
+                               pathcomp.type = UDF_PATH_COMP_MOUNTROOT;
+                               target += mntonnamelen;
+                       }
+               } else {
+                       target++;
+               }
+
+               memcpy(pathpos, &pathcomp, len);
+               pathpos += len;
+               pathlen += len;
+       }
+
+       error = 0;
+       while (*target) {
+               /* ignore multiple '/' */
+               while (*target == '/') {
+                       target++;
+               }
+               if (!*target)
+                       break;
+
+               /* extract component name */
+               compnamelen = 0;
+               compnamepos = target;
+               while ((*target) && (*target != '/')) {
+                       target++;
+                       compnamelen++;
+               }
+
+               /* just trunc if too long ?? (security issue) */
+               if (compnamelen >= 127) {
+                       error = ENAMETOOLONG;
+                       break;
+               }
+
+               /* convert unix name to UDF name */
+               len = sizeof(struct pathcomp);
+               memset(&pathcomp, 0, len);
+               pathcomp.type = UDF_PATH_COMP_NAME;
+               len = UDF_PATH_COMP_SIZE;
+
+               if ((compnamelen == 2) && (strncmp(compnamepos, "..", 2) == 0))
+                       pathcomp.type = UDF_PATH_COMP_PARENTDIR;
+               if ((compnamelen == 1) && (*compnamepos == '.'))
+                       pathcomp.type = UDF_PATH_COMP_CURDIR;
+
+               if (pathcomp.type == UDF_PATH_COMP_NAME) {
+                       unix_to_udf_name(
+                               (char *) &pathcomp.ident, &pathcomp.l_ci,
+                               compnamepos, compnamelen,
+                               &udf_node->ump->logical_vol->desc_charset);
+                       len = UDF_PATH_COMP_SIZE + pathcomp.l_ci;
+               }
+
+               if (pathlen + len >= UDF_SYMLINKBUFLEN) {
+                       error = ENAMETOOLONG;
+                       break;
+               }
+
+               memcpy(pathpos, &pathcomp, len);
+               pathpos += len;
+               pathlen += len;
+       }
+
+       if (error) {
+               /* aparently too big */
+               free(pathbuf, M_UDFTEMP);
+               return error;
+       }
+
+       error = udf_grow_node(udf_node, pathlen);
+       if (error) {
+               /* failed to pregrow node */
+               free(pathbuf, M_UDFTEMP);
+               return error;
+       }
+
+       /* write out structure on the new file */
+       error = vn_rdwr(UIO_WRITE, udf_node->vnode,
+               pathbuf, pathlen, 0,
+               UIO_SYSSPACE, IO_NODELOCKED | IO_ALTSEMANTICS,
+               FSCRED, NULL, NULL);
+
+       /* return status of symlink contents writeout */
+       free(pathbuf, M_UDFTEMP);
+       return error;
+}
+
+
+int
+udf_symlink(void *v)
+{
+       struct vop_symlink_args /* {
+               struct vnode *a_dvp;
+               struct vnode **a_vpp;
+               struct componentname *a_cnp;
+               struct vattr *a_vap;
+               char *a_target;
+       } */ *ap = v;
+       struct vnode  *dvp = ap->a_dvp;
+       struct vnode **vpp = ap->a_vpp;
+       struct vattr  *vap  = ap->a_vap;
+       struct componentname *cnp = ap->a_cnp;
+       struct udf_node *dir_node;
+       struct udf_node *udf_node;
+       int error;
+
+       DPRINTF(CALL, ("udf_symlink called\n"));
+       DPRINTF(CALL, ("\tlinking to `%s`\n",  ap->a_target));
+       error = udf_create_node(dvp, vpp, vap, cnp);
+       KASSERT(((error == 0) && (*vpp != NULL)) || ((error && (*vpp == NULL))));
+       if (!error) {
+               dir_node = VTOI(dvp);
+               udf_node = VTOI(*vpp);
+               KASSERT(udf_node);
+               error = udf_do_symlink(udf_node, ap->a_target);
+               if (error) {
+                       /* remove node */
+                       udf_shrink_node(udf_node, 0);
+                       udf_dir_detach(udf_node->ump, dir_node, udf_node, cnp);
+               }
+       }
+       vput(dvp);
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_readlink(void *v)
+{
+       struct vop_readlink_args /* {
+               struct vnode *a_vp;
+               struct uio *a_uio;
+               kauth_cred_t a_cred;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct uio *uio = ap->a_uio;
+       kauth_cred_t cred = ap->a_cred;
+       struct udf_node *udf_node;
+       struct pathcomp pathcomp;
+       struct vattr vattr;
+       uint8_t *pathbuf, *targetbuf, *tmpname;
+       uint8_t *pathpos, *targetpos;
+       char *mntonname;
+       int pathlen, targetlen, namelen, mntonnamelen, len, l_ci;
+       int first, error;
+
+       DPRINTF(CALL, ("udf_readlink called\n"));
+
+       udf_node = VTOI(vp);
+       error = VOP_GETATTR(vp, &vattr, cred);
+       if (error)
+               return error;
+
+       /* claim temporary buffers for translation */
+       pathbuf   = malloc(UDF_SYMLINKBUFLEN, M_UDFTEMP, M_WAITOK);
+       targetbuf = malloc(PATH_MAX+1, M_UDFTEMP, M_WAITOK);
+       tmpname   = malloc(PATH_MAX+1, M_UDFTEMP, M_WAITOK);
+       memset(pathbuf, 0, UDF_SYMLINKBUFLEN);
+       memset(targetbuf, 0, PATH_MAX);
+
+       /* read contents of file in our temporary buffer */
+       error = vn_rdwr(UIO_READ, udf_node->vnode,
+               pathbuf, vattr.va_size, 0,
+               UIO_SYSSPACE, IO_NODELOCKED | IO_ALTSEMANTICS,
+               FSCRED, NULL, NULL);
+       if (error) {
+               /* failed to read in symlink contents */
+               free(pathbuf, M_UDFTEMP);
+               free(targetbuf, M_UDFTEMP);
+               free(tmpname, M_UDFTEMP);
+               return error;
+       }
+
+       /* convert to a unix path */
+       pathpos   = pathbuf;
+       pathlen   = 0;
+       targetpos = targetbuf;
+       targetlen = PATH_MAX;
+       mntonname    = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname;
+       mntonnamelen = strlen(mntonname);
+
+       error = 0;
+       first = 1;
+       while (vattr.va_size - pathlen >= UDF_PATH_COMP_SIZE) {
+               len = UDF_PATH_COMP_SIZE;
+               memcpy(&pathcomp, pathpos, len);
+               l_ci = pathcomp.l_ci;
+               switch (pathcomp.type) {
+               case UDF_PATH_COMP_ROOT :
+                       /* XXX should check for l_ci; bugcompatible now */
+                       if ((targetlen < 1) || !first) {
+                               error = EINVAL;
+                               break;
+                       }
+                       *targetpos++ = '/'; targetlen--;
+                       break;
+               case UDF_PATH_COMP_MOUNTROOT :
+                       /* XXX what should it be if l_ci > 0 ? [4/48.16.1.2] */
+                       if (l_ci || (targetlen < mntonnamelen+1) || !first) {
+                               error = EINVAL;
+                               break;
+                       }
+                       memcpy(targetpos, mntonname, mntonnamelen);
+                       targetpos += mntonnamelen; targetlen -= mntonnamelen;
+                       if (vattr.va_size-pathlen > UDF_PATH_COMP_SIZE+l_ci) {
+                               /* more follows, so must be directory */
+                               *targetpos++ = '/'; targetlen--;
+                       }
+                       break;
+               case UDF_PATH_COMP_PARENTDIR :
+                       /* XXX should check for l_ci; bugcompatible now */
+                       if (targetlen < 3) {
+                               error = EINVAL;
+                               break;
+                       }
+                       *targetpos++ = '.'; targetlen--;
+                       *targetpos++ = '.'; targetlen--;
+                       *targetpos++ = '/'; targetlen--;
+                       break;
+               case UDF_PATH_COMP_CURDIR :
+                       /* XXX should check for l_ci; bugcompatible now */
+                       if (targetlen < 2) {
+                               error = EINVAL;
+                               break;
+                       }
+                       *targetpos++ = '.'; targetlen--;
+                       *targetpos++ = '/'; targetlen--;
+                       break;
+               case UDF_PATH_COMP_NAME :
+                       if (l_ci == 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       memset(tmpname, 0, PATH_MAX);
+                       memcpy(&pathcomp, pathpos, len + l_ci);
+                       udf_to_unix_name(tmpname, MAXPATHLEN,
+                               pathcomp.ident, l_ci,
+                               &udf_node->ump->logical_vol->desc_charset);
+                       namelen = strlen(tmpname);
+                       if (targetlen < namelen + 1) {
+                               error = EINVAL;
+                               break;
+                       }
+                       memcpy(targetpos, tmpname, namelen);
+                       targetpos += namelen; targetlen -= namelen;
+                       if (vattr.va_size-pathlen > UDF_PATH_COMP_SIZE+l_ci) {
+                               /* more follows, so must be directory */
+                               *targetpos++ = '/'; targetlen--;
+                       }
+                       break;
+               default :
+                       error = EINVAL;
+                       break;
+               }
+               first = 0;
+               if (error)
+                       break;
+               pathpos += UDF_PATH_COMP_SIZE + l_ci;
+               pathlen += UDF_PATH_COMP_SIZE + l_ci;
+
+       }
+       /* all processed? */
+       if (vattr.va_size - pathlen > 0)
+               error = EINVAL;
+
+       /* uiomove() to destination */
+       if (!error)
+               uiomove(targetbuf, PATH_MAX - targetlen, uio);
+
+       free(pathbuf, M_UDFTEMP);
+       free(targetbuf, M_UDFTEMP);
+       free(tmpname, M_UDFTEMP);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * udf_rename() moved to udf_rename.c
+ */
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_remove(void *v)
+{
+       struct vop_remove_args /* {
+               struct vnode *a_dvp;
+               struct vnode *a_vp;
+               struct componentname *a_cnp;
+       } */ *ap = v;
+       struct vnode *dvp = ap->a_dvp;
+       struct vnode *vp  = ap->a_vp;
+       struct componentname *cnp = ap->a_cnp;
+       struct udf_node *dir_node = VTOI(dvp);
+       struct udf_node *udf_node = VTOI(vp);
+       struct udf_mount *ump = dir_node->ump;
+       int error;
+
+       DPRINTF(CALL, ("udf_remove called\n"));
+       if (vp->v_type != VDIR) {
+               error = udf_dir_detach(ump, dir_node, udf_node, cnp);
+               DPRINTFIF(NODE, error, ("\tgot error removing file\n"));
+       } else {
+               DPRINTF(NODE, ("\tis a directory: perm. denied\n"));
+               error = EPERM;
+       }
+
+       if (error == 0) {
+               VN_KNOTE(vp, NOTE_DELETE);
+               VN_KNOTE(dvp, NOTE_WRITE);
+       }
+
+       if (dvp == vp)
+               vrele(vp);
+       else
+               vput(vp);
+       vput(dvp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_rmdir(void *v)
+{
+       struct vop_rmdir_args /* {
+               struct vnode *a_dvp;
+               struct vnode *a_vp;
+               struct componentname *a_cnp;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct vnode *dvp = ap->a_dvp;
+       struct componentname *cnp = ap->a_cnp;
+       struct udf_node *dir_node = VTOI(dvp);
+       struct udf_node *udf_node = VTOI(vp);
+       struct udf_mount *ump = dir_node->ump;
+       int error, isempty;
+
+       DPRINTF(NOTIMPL, ("udf_rmdir '%s' called\n", cnp->cn_nameptr));
+
+       /* don't allow '.' to be deleted */
+       if (dir_node == udf_node) {
+               vrele(dvp);
+               vput(vp);
+               return EINVAL;
+       }
+
+       /* make sure our `leaf' node's hash is populated */
+       dirhash_get(&udf_node->dir_hash);
+       error = udf_dirhash_fill(udf_node);
+       if (error) {
+               dirhash_put(udf_node->dir_hash);
+               return error;
+       }
+
+       /* check to see if the directory is empty */
+       isempty = dirhash_dir_isempty(udf_node->dir_hash);
+       dirhash_put(udf_node->dir_hash);
+
+       if (!isempty) {
+               vput(dvp);
+               vput(vp);
+               return ENOTEMPTY;
+       }
+
+       /* detach the node from the directory, udf_node is an empty dir here */
+       error = udf_dir_detach(ump, dir_node, udf_node, cnp);
+       if (error == 0) {
+               cache_purge(vp);
+//             cache_purge(dvp);       /* XXX from msdosfs, why? */
+               /*
+                * Bug alert: we need to remove '..' from the detaching
+                * udf_node so further lookups of this are not possible. This
+                * prevents a process in a deleted directory from going to its
+                * deleted parent. Since `udf_node' is garanteed to be empty
+                * here, trunc it so no fids are there.
+                */
+               dirhash_purge(&udf_node->dir_hash);
+               udf_shrink_node(udf_node, 0);
+               VN_KNOTE(vp, NOTE_DELETE);
+       }
+       DPRINTFIF(NODE, error, ("\tgot error removing dir\n"));
+
+       /* unput the nodes and exit */
+       vput(dvp);
+       vput(vp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_fsync(void *v)
+{
+       struct vop_fsync_args /* {
+               struct vnode *a_vp;
+               kauth_cred_t a_cred;
+               int a_flags;
+               off_t offlo;
+               off_t offhi;
+               struct proc *a_p;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct udf_node *udf_node = VTOI(vp);
+       int error, flags, wait;
+
+       DPRINTF(SYNC, ("udf_fsync called on %p : %s, %s\n",
+               udf_node,
+               (ap->a_flags & FSYNC_WAIT)     ? "wait":"no wait",
+               (ap->a_flags & FSYNC_DATAONLY) ? "data_only":"complete"));
+
+       /* flush data and wait for it when requested */
+       wait = (ap->a_flags & FSYNC_WAIT) ? UPDATE_WAIT : 0;
+       error = vflushbuf(vp, ap->a_flags);
+       if (error)
+               return error;
+
+       if (udf_node == NULL) {
+               printf("udf_fsync() called on NULL udf_node!\n");
+               return 0;
+       }
+       if (vp->v_tag != VT_UDF) {
+               printf("udf_fsync() called on node not tagged as UDF node!\n");
+               return 0;
+       }
+
+       /* set our times */
+       udf_itimes(udf_node, NULL, NULL, NULL);
+
+       /* if called when mounted readonly, never write back */
+       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+               return 0;
+
+       /* if only data is requested, return */
+       if (ap->a_flags & FSYNC_DATAONLY)
+               return 0;
+
+       /* check if the node is dirty 'enough'*/
+       flags = udf_node->i_flags & (IN_MODIFIED | IN_ACCESSED);
+       if (flags == 0)
+               return 0;
+
+       /* if we don't have to wait, check for IO pending */
+       if (!wait) {
+               if (vp->v_numoutput > 0) {
+                       DPRINTF(SYNC, ("udf_fsync %p, rejecting on v_numoutput\n", udf_node));
+                       return 0;
+               }
+               if (udf_node->outstanding_bufs > 0) {
+                       DPRINTF(SYNC, ("udf_fsync %p, rejecting on outstanding_bufs\n", udf_node));
+                       return 0;
+               }
+               if (udf_node->outstanding_nodedscr > 0) {
+                       DPRINTF(SYNC, ("udf_fsync %p, rejecting on outstanding_nodedscr\n", udf_node));
+                       return 0;
+               }
+       }
+
+       /* wait until vp->v_numoutput reaches zero i.e. is finished */
+       if (wait) {
+               DPRINTF(SYNC, ("udf_fsync %p, waiting\n", udf_node));
+               mutex_enter(vp->v_interlock);
+               while (vp->v_numoutput) {
+                       DPRINTF(SYNC, ("udf_fsync %p, v_numoutput %d\n", udf_node, vp->v_numoutput));
+                       cv_timedwait(&vp->v_cv, vp->v_interlock, hz/8);
+               }
+               mutex_exit(vp->v_interlock);
+               DPRINTF(SYNC, ("udf_fsync %p, fin wait\n", udf_node));
+       }
+
+       /* write out node and wait for it if requested */
+       DPRINTF(SYNC, ("udf_fsync %p, writeout node\n", udf_node));
+       error = udf_writeout_node(udf_node, wait);
+       if (error)
+               return error;
+
+       /* TODO/XXX if ap->a_flags & FSYNC_CACHE, we ought to do a disc sync */
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_advlock(void *v)
+{
+       struct vop_advlock_args /* {
+               struct vnode *a_vp;
+               void *a_id;
+               int a_op;
+               struct flock *a_fl;
+               int a_flags;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct udf_node *udf_node = VTOI(vp);
+       struct file_entry    *fe;
+       struct extfile_entry *efe;
+       uint64_t file_size;
+
+       DPRINTF(LOCKING, ("udf_advlock called\n"));
+
+       /* get directory filesize */
+       if (udf_node->fe) {
+               fe = udf_node->fe;
+               file_size = udf_rw64(fe->inf_len);
+       } else {
+               assert(udf_node->efe);
+               efe = udf_node->efe;
+               file_size = udf_rw64(efe->inf_len);
+       }
+
+       return lf_advlock(ap, &udf_node->lockf, file_size);
+}
+
+/* --------------------------------------------------------------------- */
+
+/* Global vfs vnode data structures for udfs */
+int (**udf_vnodeop_p)(void *);
+
+const struct vnodeopv_entry_desc udf_vnodeop_entries[] = {
+       { &vop_default_desc, vn_default_error },
+       { &vop_lookup_desc, udf_lookup },       /* lookup */
+       { &vop_create_desc, udf_create },       /* create */
+       { &vop_mknod_desc, udf_mknod },         /* mknod */     /* TODO */
+       { &vop_open_desc, udf_open },           /* open */
+       { &vop_close_desc, udf_close },         /* close */
+       { &vop_access_desc, udf_access },       /* access */
+       { &vop_getattr_desc, udf_getattr },     /* getattr */
+       { &vop_setattr_desc, udf_setattr },     /* setattr */   /* TODO chflags */
+       { &vop_read_desc, udf_read },           /* read */
+       { &vop_write_desc, udf_write },         /* write */     /* WRITE */
+       { &vop_fcntl_desc, genfs_fcntl },       /* fcntl */     /* TODO? */
+       { &vop_ioctl_desc, genfs_enoioctl },    /* ioctl */     /* TODO? */
+       { &vop_poll_desc, genfs_poll },         /* poll */      /* TODO/OK? */
+       { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */  /* ? */
+       { &vop_revoke_desc, genfs_revoke },     /* revoke */    /* TODO? */
+       { &vop_mmap_desc, genfs_mmap },         /* mmap */      /* OK? */
+       { &vop_fsync_desc, udf_fsync },         /* fsync */
+       { &vop_seek_desc, genfs_seek },         /* seek */
+       { &vop_remove_desc, udf_remove },       /* remove */
+       { &vop_link_desc, udf_link },           /* link */      /* TODO */
+       { &vop_rename_desc, udf_rename },       /* rename */    /* TODO */
+       { &vop_mkdir_desc, udf_mkdir },         /* mkdir */ 
+       { &vop_rmdir_desc, udf_rmdir },         /* rmdir */
+       { &vop_symlink_desc, udf_symlink },     /* symlink */   /* TODO */
+       { &vop_readdir_desc, udf_readdir },     /* readdir */
+       { &vop_readlink_desc, udf_readlink },   /* readlink */  /* TEST ME */
+       { &vop_abortop_desc, genfs_abortop },   /* abortop */   /* TODO/OK? */
+       { &vop_inactive_desc, udf_inactive },   /* inactive */
+       { &vop_reclaim_desc, udf_reclaim },     /* reclaim */
+       { &vop_lock_desc, genfs_lock },         /* lock */
+       { &vop_unlock_desc, genfs_unlock },     /* unlock */
+       { &vop_bmap_desc, udf_trivial_bmap },   /* bmap */      /* 1:1 bmap */
+       { &vop_strategy_desc, udf_vfsstrategy },/* strategy */
+/*     { &vop_print_desc, udf_print }, */      /* print */
+       { &vop_islocked_desc, genfs_islocked }, /* islocked */
+       { &vop_pathconf_desc, udf_pathconf },   /* pathconf */
+       { &vop_advlock_desc, udf_advlock },     /* advlock */   /* TEST ME */
+       { &vop_bwrite_desc, vn_bwrite },        /* bwrite */    /* ->strategy */
+       { &vop_getpages_desc, genfs_getpages }, /* getpages */
+       { &vop_putpages_desc, genfs_putpages }, /* putpages */
+       { NULL, NULL }
+};
+
+
+const struct vnodeopv_desc udf_vnodeop_opv_desc = {
+       &udf_vnodeop_p, udf_vnodeop_entries
+};
diff --git a/sys/fs/v7fs/Makefile b/sys/fs/v7fs/Makefile
new file mode 100644 (file)
index 0000000..e677fa3
--- /dev/null
@@ -0,0 +1,7 @@
+#      $NetBSD: Makefile,v 1.1 2011/06/27 11:52:24 uch Exp $
+
+INCSDIR= /usr/include/fs/v7fs
+
+INCS= v7fs.h v7fs_args.h
+
+.include <bsd.kinc.mk>
diff --git a/sys/fs/v7fs/files.v7fs b/sys/fs/v7fs/files.v7fs
new file mode 100644 (file)
index 0000000..509253f
--- /dev/null
@@ -0,0 +1,22 @@
+#      $NetBSD: files.v7fs,v 1.1 2011/06/27 11:52:24 uch Exp $
+
+deffs  V7FS
+defflag        opt_v7fs.h                      V7FS_EI
+
+# Core. OS independent. These files are used by userland program.(fsck,newfs)
+file   fs/v7fs/v7fs_endian.c           v7fs
+file   fs/v7fs/v7fs_superblock.c       v7fs
+file   fs/v7fs/v7fs_inode.c            v7fs
+file   fs/v7fs/v7fs_dirent.c           v7fs
+file   fs/v7fs/v7fs_datablock.c        v7fs
+file   fs/v7fs/v7fs_file.c             v7fs
+file   fs/v7fs/v7fs_io.c               v7fs
+# util.
+file   fs/v7fs/v7fs_file_util.c        v7fs
+file   fs/v7fs/v7fs_inode_util.c       v7fs
+file   fs/v7fs/v7fs_superblock_util.c  v7fs
+# OS glue
+file   fs/v7fs/v7fs_io_kern.c          v7fs
+file   fs/v7fs/v7fs_extern.c           v7fs
+file   fs/v7fs/v7fs_vnops.c            v7fs
+file   fs/v7fs/v7fs_vfsops.c           v7fs
diff --git a/sys/fs/v7fs/v7fs.h b/sys/fs/v7fs/v7fs.h
new file mode 100644 (file)
index 0000000..c35bace
--- /dev/null
@@ -0,0 +1,180 @@
+/*     $NetBSD: v7fs.h,v 1.2 2011/07/16 12:35:40 uch Exp $     */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _V7FS_H_
+/* 7th Edition of Unix(PDP-11) Filesystem definition. */
+#define        _V7FS_H_
+#include <sys/types.h>
+#ifndef _KERNEL
+#include <inttypes.h>
+#endif
+/*
+ *   V7 File System
+ *
+ *     +------------------
+ *     |Boot block (512byte)   sector [0]
+ *     |
+ *     +------------------
+ *     |Super block (512byte)  sector [1]
+ *     |
+ *     +------------------
+ *     |v7fs_inode(64byte       sector [2]
+ *         .
+ *         .
+ *     |
+ *     +------------------
+ *     |data block              sector [datablock_start_sector]
+ *     |
+ *         .
+ *         .
+ *     |
+ *     +------------------
+ *                              <-    [sector volume_size]
+ *
+ *     |
+ *     +------------------     volume size.
+ *
+ * Max volume size is 8GB (24bit daddr_t)
+ * Max file size is ~1GB
+ *
+ */
+
+/* V7 type. */
+typedef uint16_t v7fs_ino_t;
+typedef uint32_t v7fs_daddr_t;
+typedef int32_t v7fs_time_t;
+typedef uint32_t v7fs_off_t;
+typedef uint16_t v7fs_dev_t;
+typedef uint16_t v7fs_mode_t;
+#define        V7FS_DADDR_MAX          0x00ffffff
+#define        V7FS_INODE_MAX          0xffff
+
+#define        V7FS_BSIZE              512
+#define        V7FS_BSHIFT             9
+#define        V7FS_ROUND_BSIZE(x)                                     \
+       ((((x) + (V7FS_BSIZE - 1)) & ~(V7FS_BSIZE - 1)))
+#define        V7FS_TRUNC_BSIZE(x)     ((x) & ~(V7FS_BSIZE - 1))
+
+#define        V7FS_RESIDUE_BSIZE(x)                                   \
+       ((x) - ((((x) - 1) >> V7FS_BSHIFT) << V7FS_BSHIFT))
+
+/* Disk location. */
+#define        V7FS_BOOTBLOCK_SECTOR   0
+#define        V7FS_SUPERBLOCK_SECTOR  1
+#define        V7FS_ILIST_SECTOR       2
+
+/* Superblock */
+/* cache. */
+#define        V7FS_MAX_FREEBLOCK      50
+#define        V7FS_MAX_FREEINODE      100
+struct v7fs_superblock {
+       /* [3 ... (datablock_start_sector-1)]are ilist */
+       uint16_t datablock_start_sector;
+       v7fs_daddr_t volume_size;
+       int16_t nfreeblock;     /* # of freeblock in superblock cache. */
+       v7fs_daddr_t freeblock[V7FS_MAX_FREEBLOCK];     /* cache. */
+       int16_t nfreeinode;     /* # of free inode in superblock cache. */
+       v7fs_ino_t freeinode[V7FS_MAX_FREEINODE];       /* cache. */
+       int8_t lock_freeblock;
+       int8_t lock_freeinode;
+       int8_t modified;
+       int8_t readonly;
+       v7fs_time_t update_time;
+       v7fs_daddr_t total_freeblock;
+       v7fs_ino_t total_freeinode;
+} __packed;
+
+/* Datablock */
+#define        V7FS_NADDR              13
+#define        V7FS_NADDR_DIRECT       10
+#define        V7FS_NADDR_INDEX1       10
+#define        V7FS_NADDR_INDEX2       11
+#define        V7FS_NADDR_INDEX3       12
+/* daddr index. */
+#define        V7FS_DADDR_PER_BLOCK    (V7FS_BSIZE / sizeof(v7fs_daddr_t))
+struct v7fs_freeblock {
+       int16_t nfreeblock;
+       v7fs_daddr_t freeblock[V7FS_MAX_FREEBLOCK];
+} __packed;
+
+
+/* Dirent */
+#define        V7FS_NAME_MAX           14
+#define        V7FS_PATH_MAX           PATH_MAX        /* No V7 limit. */
+#define        V7FS_LINK_MAX           LINK_MAX        /* No V7 limit. */
+struct v7fs_dirent {
+       v7fs_ino_t inode_number;
+       char name[V7FS_NAME_MAX];
+} __packed;    /*16byte */
+
+/* Inode */
+#define        V7FS_BALBLK_INODE       1       /* monument */
+#define        V7FS_ROOT_INODE         2
+#define        V7FS_MAX_INODE(s)                                               \
+       (((s)->datablock_start_sector - V7FS_ILIST_SECTOR) *            \
+       V7FS_BSIZE / sizeof(struct v7fs_inode_diskimage))
+#define        V7FS_INODE_PER_BLOCK                                            \
+       (V7FS_BSIZE / sizeof(struct v7fs_inode_diskimage))
+#define        V7FS_ILISTBLK_MAX       (V7FS_INODE_MAX / V7FS_INODE_PER_BLOCK)
+
+struct v7fs_inode_diskimage {
+       int16_t mode;
+       int16_t nlink;  /* [DIR] # of child directories. [REG] link count. */
+       int16_t uid;
+       int16_t gid;
+       v7fs_off_t filesize;    /* byte */
+#define        V7FS_DINODE_ADDR_LEN    40
+       /* 39 used; 13 addresses of 3 byte each. */
+       uint8_t addr[V7FS_DINODE_ADDR_LEN];
+       /*for device node: addr[0] is major << 8 | minor. */
+       v7fs_time_t atime;
+       v7fs_time_t mtime;
+       v7fs_time_t ctime;
+} __packed;    /*64byte */
+
+/* File type */
+#define        V7FS_IFMT       0170000 /* File type mask */
+#define        V7FS_IFCHR      0020000 /* charcter device */
+#define        V7FS_IFDIR      0040000 /* directory */
+#define        V7FS_IFBLK      0060000 /* block device */
+#define        V7FS_IFREG      0100000 /* file. */
+/* Obsoleted file type. */
+#define        V7FS_IFMPC      0030000 /* multiplexed char special */
+#define        V7FS_IFMPB      0070000 /* multiplexed block special */
+/* Don't apear original V7 filesystem. Found at 2.10BSD. */
+#define        V7FSBSD_IFLNK   0120000 /* symbolic link */
+#define        V7FSBSD_IFSOCK  0140000 /* socket */
+/* Don't apear original V7 filesystem. NetBSD. */
+#define        V7FSBSD_IFFIFO  0010000 /* Named pipe. */
+
+#define        V7FSBSD_MAXSYMLINKLEN   V7FS_BSIZE
+
+#endif /*!_V7FS_H_ */
diff --git a/sys/fs/v7fs/v7fs_args.h b/sys/fs/v7fs/v7fs_args.h
new file mode 100644 (file)
index 0000000..017518c
--- /dev/null
@@ -0,0 +1,40 @@
+/*     $NetBSD: v7fs_args.h,v 1.1 2011/06/27 11:52:24 uch Exp $ */
+
+/*-
+ * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _FS_V7FS_V7FS_ARGS_H_
+#define        _FS_V7FS_V7FS_ARGS_H_
+
+struct v7fs_args {
+       char    *fspec;         /* blocks special holding the fs to mount */
+       int     endian;         /* target filesystem endian */
+};
+
+#endif /* _FS_V7FS_V7FS_ARGS_H_ */
diff --git a/sys/fs/v7fs/v7fs_datablock.c b/sys/fs/v7fs/v7fs_datablock.c
new file mode 100644 (file)
index 0000000..cd84521
--- /dev/null
@@ -0,0 +1,729 @@
+/*     $NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $   */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#include <sys/types.h>
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#endif
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_endian.h"
+#include "v7fs_inode.h"
+#include "v7fs_datablock.h"
+#include "v7fs_superblock.h"
+
+#ifdef V7FS_DATABLOCK_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#endif
+
+static int v7fs_datablock_deallocate(struct v7fs_self *, v7fs_daddr_t);
+static int v7fs_loop1(struct v7fs_self *, v7fs_daddr_t, size_t *,
+    int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
+static int v7fs_loop2(struct v7fs_self *, v7fs_daddr_t, size_t *,
+    int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
+static v7fs_daddr_t v7fs_link(struct v7fs_self *, v7fs_daddr_t, int);
+static v7fs_daddr_t v7fs_add_leaf(struct v7fs_self *, v7fs_daddr_t, int);
+static v7fs_daddr_t v7fs_unlink(struct v7fs_self *, v7fs_daddr_t, int);
+static v7fs_daddr_t v7fs_remove_leaf(struct v7fs_self *, v7fs_daddr_t, int);
+static v7fs_daddr_t v7fs_remove_self(struct v7fs_self *, v7fs_daddr_t);
+
+#ifdef V7FS_DATABLOCK_DEBUG
+void daddr_map_dump(const struct v7fs_daddr_map *);
+#else
+#define        daddr_map_dump(x)       ((void)0)
+#endif
+
+bool
+datablock_number_sanity(const struct v7fs_self *fs, v7fs_daddr_t blk)
+{
+       const struct v7fs_superblock *sb = &fs->superblock;
+       bool ok = (blk >= sb->datablock_start_sector) &&
+           (blk < sb->volume_size);
+
+#ifdef V7FS_DATABLOCK_DEBUG
+       if (!ok) {
+               DPRINTF("Bad data block #%d\n", blk);
+       }
+#endif
+
+       return ok;
+}
+
+int
+v7fs_datablock_allocate(struct v7fs_self *fs, v7fs_daddr_t *block_number)
+{
+       struct v7fs_superblock *sb = &fs->superblock;
+       v7fs_daddr_t blk;
+       int error = 0;
+
+       *block_number = 0;
+       SUPERB_LOCK(fs);
+       do {
+               if (!sb->total_freeblock) {
+                       DPRINTF("free block exhausted!!!\n");
+                       SUPERB_UNLOCK(fs);
+                       return ENOSPC;
+               }
+
+               /* Get free block from superblock cache. */
+               blk = sb->freeblock[--sb->nfreeblock];
+               sb->total_freeblock--;
+               sb->modified = 1;
+
+               /* If nfreeblock is zero, it block is next freeblock link. */
+               if (sb->nfreeblock == 0) {
+                       if ((error = v7fs_freeblock_update(fs, blk))) {
+                               DPRINTF("no freeblock!!!\n");
+                               SUPERB_UNLOCK(fs);
+                               return error;
+                       }
+                       /* This freeblock link is no longer required. */
+                       /* use as data block. */
+               }
+       } while (!datablock_number_sanity(fs, blk)); /* skip bogus block. */
+       SUPERB_UNLOCK(fs);
+
+       DPRINTF("Get freeblock %d\n", blk);
+       /* Zero clear datablock. */
+       void *buf;
+       if (!(buf = scratch_read(fs, blk)))
+               return EIO;
+       memset(buf, 0, V7FS_BSIZE);
+       if (!fs->io.write(fs->io.cookie, buf, blk))
+               error = EIO;
+       scratch_free(fs, buf);
+
+       if (error == 0)
+               *block_number = blk;
+
+       return error;
+}
+
+static int
+v7fs_datablock_deallocate(struct v7fs_self *fs, v7fs_daddr_t blk)
+{
+       struct v7fs_superblock *sb = &fs->superblock;
+       void *buf;
+       int error = 0;
+
+       if (!datablock_number_sanity(fs, blk))
+               return EIO;
+
+       /* Add to in-core freelist. */
+       SUPERB_LOCK(fs);
+       if (sb->nfreeblock < V7FS_MAX_FREEBLOCK) {
+               sb->freeblock[sb->nfreeblock++] = blk;
+               sb->total_freeblock++;
+               sb->modified = 1;
+               DPRINTF("n_freeblock=%d\n", sb->total_freeblock);
+               SUPERB_UNLOCK(fs);
+               return 0;
+       }
+
+       /* No space to push. */
+       /* Make this block to freeblock list.and current cache moved to this. */
+       if (!(buf = scratch_read(fs, blk))) {
+               SUPERB_UNLOCK(fs);
+               return EIO;     /* Fatal */
+       }
+
+       struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf;
+       fb->nfreeblock = V7FS_MAX_FREEBLOCK;
+       int i;
+       for (i = 0; i < V7FS_MAX_FREEBLOCK; i++)
+               fb->freeblock[i] = V7FS_VAL32(fs, sb->freeblock[i]);
+
+       if (!fs->io.write(fs->io.cookie, (uint8_t *)fb, blk)) {
+               error =  EIO;   /* Fatal */
+       } else {
+               /* Link. on next allocate, this block is used as datablock, */
+               /* and swap outed freeblock list is restored. */
+               sb->freeblock[0] = blk;
+               sb->nfreeblock = 1;
+               sb->total_freeblock++;
+               sb->modified = 1;
+               DPRINTF("n_freeblock=%d\n", sb->total_freeblock);
+       }
+       SUPERB_UNLOCK(fs);
+       scratch_free(fs, buf);
+
+       return error;
+}
+
+int
+v7fs_datablock_addr(size_t sz, struct v7fs_daddr_map *map)
+{
+#define        NIDX            V7FS_DADDR_PER_BLOCK
+#define        DIRECT_SZ       (V7FS_NADDR_DIRECT * V7FS_BSIZE)
+#define        IDX1_SZ         (NIDX * V7FS_BSIZE)
+#define        IDX2_SZ         (NIDX * NIDX * V7FS_BSIZE)
+#define        ROUND(x, a)     ((((x) + ((a) - 1)) & ~((a) - 1)))
+       if (!sz) {
+               map->level = 0;
+               map->index[0] = 0;
+               return 0;
+       }
+
+       sz = V7FS_ROUND_BSIZE(sz);
+
+       /* Direct */
+       if (sz <= DIRECT_SZ) {
+               map->level = 0;
+               map->index[0] = (sz >> V7FS_BSHIFT) - 1;
+               return 0;
+       }
+       /* Index 1 */
+       sz -= DIRECT_SZ;
+
+       if (sz <= IDX1_SZ) {
+               map->level = 1;
+               map->index[0] = (sz >> V7FS_BSHIFT) - 1;
+               return 0;
+       }
+       sz -= IDX1_SZ;
+
+       /* Index 2 */
+       if (sz <= IDX2_SZ) {
+               map->level = 2;
+               map->index[0] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1;
+               map->index[1] = ((sz - (map->index[0] * IDX1_SZ)) >>
+                   V7FS_BSHIFT) - 1;
+               return 0;
+       }
+       sz -= IDX2_SZ;
+
+       /* Index 3 */
+       map->level = 3;
+       map->index[0] = ROUND(sz, IDX2_SZ) / IDX2_SZ - 1;
+       sz -= map->index[0] * IDX2_SZ;
+       map->index[1] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1;
+       sz -= map->index[1] * IDX1_SZ;
+       map->index[2] = (sz >> V7FS_BSHIFT) - 1;
+
+       return map->index[2] >= NIDX ? ENOSPC : 0;
+}
+
+int
+v7fs_datablock_foreach(struct v7fs_self *fs, struct v7fs_inode *p,
+    int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
+{
+       size_t i;
+       v7fs_daddr_t blk, blk2;
+       size_t filesize;
+       bool last;
+       int ret;
+
+       if (!(filesize = v7fs_inode_filesize(p)))
+               return V7FS_ITERATOR_END;
+#ifdef V7FS_DATABLOCK_DEBUG
+       size_t sz = filesize;
+#endif
+
+       /* Direct */
+       for (i = 0; i < V7FS_NADDR_DIRECT; i++, filesize -= V7FS_BSIZE) {
+               blk = p->addr[i];
+               if (!datablock_number_sanity(fs, blk)) {
+                       DPRINTF("inode#%d direct=%zu filesize=%zu\n",
+                           p->inode_number, i, sz);
+                       return EIO;
+               }
+
+               last = filesize <= V7FS_BSIZE;
+               if ((ret = func(fs, ctx, blk, last ? filesize : V7FS_BSIZE)))
+                       return ret;
+               if (last)
+                       return V7FS_ITERATOR_END;
+       }
+
+       /* Index 1 */
+       blk = p->addr[V7FS_NADDR_INDEX1];
+       if (!datablock_number_sanity(fs, blk))
+               return EIO;
+
+       if ((ret = v7fs_loop1(fs, blk, &filesize, func, ctx)))
+               return ret;
+
+       /* Index 2 */
+       blk = p->addr[V7FS_NADDR_INDEX2];
+       if (!datablock_number_sanity(fs, blk))
+               return EIO;
+
+       if ((ret = v7fs_loop2(fs, blk, &filesize, func, ctx)))
+               return ret;
+
+       /* Index 3 */
+       blk = p->addr[V7FS_NADDR_INDEX3];
+       if (!datablock_number_sanity(fs, blk))
+               return EIO;
+
+       for (i = 0; i < V7FS_DADDR_PER_BLOCK; i++) {
+               blk2 = v7fs_link(fs, blk, i);
+               if (!datablock_number_sanity(fs, blk))
+                       return EIO;
+
+               if ((ret = v7fs_loop2(fs, blk2, &filesize, func, ctx)))
+                       return ret;
+       }
+
+       return EFBIG;
+}
+
+static int
+v7fs_loop2(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize,
+    int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
+{
+       v7fs_daddr_t blk;
+       int ret;
+       size_t j;
+
+       for (j = 0; j < V7FS_DADDR_PER_BLOCK; j++) {
+               blk = v7fs_link(fs, listblk, j);
+               if (!datablock_number_sanity(fs, blk))
+                       return EIO;
+               if ((ret = v7fs_loop1(fs, blk, filesize, func, ctx)))
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int
+v7fs_loop1(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize,
+    int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
+{
+       v7fs_daddr_t blk;
+       bool last;
+       int ret;
+       size_t k;
+
+       for (k = 0; k < V7FS_DADDR_PER_BLOCK; k++, *filesize -= V7FS_BSIZE) {
+               blk = v7fs_link(fs, listblk, k);
+               if (!datablock_number_sanity(fs, blk))
+                       return EIO;
+               last = *filesize <= V7FS_BSIZE;
+               if ((ret = func(fs, ctx, blk, last ? *filesize : V7FS_BSIZE)))
+                       return ret;
+               if (last)
+                       return V7FS_ITERATOR_END;
+       }
+
+       return 0;
+}
+
+v7fs_daddr_t
+v7fs_datablock_last(struct v7fs_self *fs, struct v7fs_inode *inode,
+    v7fs_off_t ofs)
+{
+       struct v7fs_daddr_map map;
+       v7fs_daddr_t blk = 0;
+       v7fs_daddr_t *addr = inode->addr;
+
+       /* Inquire last data block location. */
+       if (v7fs_datablock_addr(ofs, &map) != 0)
+               return 0;
+
+       switch (map.level)
+       {
+       case 0: /*Direct */
+               blk = inode->addr[map.index[0]];
+               break;
+       case 1: /*Index1 */
+               blk = v7fs_link(fs, addr[V7FS_NADDR_INDEX1], map.index[0]);
+               break;
+       case 2: /*Index2 */
+               blk = v7fs_link(fs, v7fs_link(fs,
+                   addr[V7FS_NADDR_INDEX2], map.index[0]), map.index[1]);
+               break;
+       case 3: /*Index3 */
+               blk = v7fs_link(fs, v7fs_link(fs, v7fs_link(fs,
+                   addr[V7FS_NADDR_INDEX3], map.index[0]), map.index[1]),
+                   map.index[2]);
+               break;
+       }
+
+       return blk;
+}
+
+int
+v7fs_datablock_expand(struct v7fs_self *fs, struct v7fs_inode *inode, size_t sz)
+{
+       size_t old_filesize = inode->filesize;
+       size_t new_filesize = old_filesize + sz;
+       struct v7fs_daddr_map oldmap, newmap;
+       v7fs_daddr_t blk, idxblk;
+       int error;
+       v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT;
+       v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT;
+
+       if (old_nblk == new_nblk) {
+               inode->filesize += sz;
+               v7fs_inode_writeback(fs, inode);
+               return 0; /* no need to expand. */
+       }
+       struct v7fs_inode backup = *inode;
+       v7fs_daddr_t required_blk = new_nblk - old_nblk;
+
+       DPRINTF("%zu->%zu, required block=%d\n", old_filesize, new_filesize,
+           required_blk);
+
+       v7fs_datablock_addr(old_filesize, &oldmap);
+       v7fs_daddr_t i;
+       for (i = 0; i < required_blk; i++) {
+               v7fs_datablock_addr(old_filesize + (i+1) * V7FS_BSIZE, &newmap);
+               daddr_map_dump(&oldmap);
+               daddr_map_dump(&newmap);
+
+               if (oldmap.level != newmap.level) {
+                       /* Allocate index area */
+                       if ((error = v7fs_datablock_allocate(fs, &idxblk)))
+                               return error;
+
+                       switch (newmap.level) {
+                       case 1:
+                               DPRINTF("0->1\n");
+                               inode->addr[V7FS_NADDR_INDEX1] = idxblk;
+                               blk = v7fs_add_leaf(fs, idxblk, 0);
+                               break;
+                       case 2:
+                               DPRINTF("1->2\n");
+                               inode->addr[V7FS_NADDR_INDEX2] = idxblk;
+                               blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs,
+                                   idxblk, 0), 0);
+                               break;
+                       case 3:
+                               DPRINTF("2->3\n");
+                               inode->addr[V7FS_NADDR_INDEX3] = idxblk;
+                               blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs,
+                                   v7fs_add_leaf(fs, idxblk, 0), 0), 0);
+                               break;
+                       }
+               } else {
+                       switch (newmap.level) {
+                       case 0:
+                               if ((error = v7fs_datablock_allocate(fs, &blk)))
+                                       return error;
+                               inode->addr[newmap.index[0]] = blk;
+                               DPRINTF("direct index %d = blk%d\n",
+                                   newmap.index[0], blk);
+                               break;
+                       case 1:
+                               idxblk = inode->addr[V7FS_NADDR_INDEX1];
+                               blk = v7fs_add_leaf(fs, idxblk,
+                                   newmap.index[0]);
+                               break;
+                       case 2:
+                               idxblk = inode->addr[V7FS_NADDR_INDEX2];
+                               if (oldmap.index[0] != newmap.index[0]) {
+                                       v7fs_add_leaf(fs, idxblk,
+                                           newmap.index[0]);
+                               }
+                               blk = v7fs_add_leaf(fs, v7fs_link(fs,idxblk,
+                                   newmap.index[0]), newmap.index[1]);
+                               break;
+                       case 3:
+                               idxblk = inode->addr[V7FS_NADDR_INDEX3];
+
+                               if (oldmap.index[0] != newmap.index[0]) {
+                                       v7fs_add_leaf(fs, idxblk,
+                                           newmap.index[0]);
+                               }
+
+                               if (oldmap.index[1] != newmap.index[1]) {
+                                       v7fs_add_leaf(fs, v7fs_link(fs, idxblk,
+                                           newmap.index[0]), newmap.index[1]);
+                               }
+                               blk = v7fs_add_leaf(fs, v7fs_link(fs,
+                                   v7fs_link(fs, idxblk, newmap.index[0]),
+                                   newmap.index[1]), newmap.index[2]);
+                               break;
+                       }
+               }
+               if (!blk) {
+                       *inode = backup; /* structure copy; */
+                       return ENOSPC;
+               }
+               oldmap = newmap;
+       }
+       inode->filesize += sz;
+       v7fs_inode_writeback(fs, inode);
+
+       return 0;
+}
+
+static v7fs_daddr_t
+v7fs_link(struct v7fs_self *fs, v7fs_daddr_t listblk, int n)
+{
+       v7fs_daddr_t *list;
+       v7fs_daddr_t blk;
+       void *buf;
+
+       if (!datablock_number_sanity(fs, listblk))
+               return 0;
+       if (!(buf = scratch_read(fs, listblk)))
+               return 0;
+       list = (v7fs_daddr_t *)buf;
+       blk = V7FS_VAL32(fs, list[n]);
+       scratch_free(fs, buf);
+
+       if (!datablock_number_sanity(fs, blk))
+               return 0;
+
+       return blk;
+}
+
+static v7fs_daddr_t
+v7fs_add_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int idx)
+{
+       v7fs_daddr_t newblk;
+       v7fs_daddr_t *daddr_list;
+       int error = 0;
+       void *buf;
+
+       if (!up)
+               return 0;
+       if (!datablock_number_sanity(fs, up))
+               return 0;
+
+       if ((error = v7fs_datablock_allocate(fs, &newblk)))
+               return 0;
+       if (!(buf = scratch_read(fs, up)))
+               return 0;
+       daddr_list = (v7fs_daddr_t *)buf;
+       daddr_list[idx] = V7FS_VAL32(fs, newblk);
+       if (!fs->io.write(fs->io.cookie, buf, up))
+               newblk = 0;
+       scratch_free(fs, buf);
+
+       return newblk;
+}
+
+int
+v7fs_datablock_contract(struct v7fs_self *fs, struct v7fs_inode *inode,
+    size_t sz)
+{
+       size_t old_filesize = inode->filesize;
+       size_t new_filesize = old_filesize - sz;
+       struct v7fs_daddr_map oldmap, newmap;
+       v7fs_daddr_t blk, idxblk;
+       int error = 0;
+       v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT;
+       v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT;
+
+       if (old_nblk == new_nblk) {
+               inode->filesize -= sz;
+               v7fs_inode_writeback(fs, inode);
+               return 0; /* no need to contract; */
+       }
+       v7fs_daddr_t erase_blk = old_nblk - new_nblk;
+
+       DPRINTF("%zu->%zu # of erased block=%d\n", old_filesize, new_filesize,
+           erase_blk);
+
+       v7fs_datablock_addr(old_filesize, &oldmap);
+       v7fs_daddr_t i;
+       for (i = 0; i < erase_blk; i++) {
+               v7fs_datablock_addr(old_filesize - (i+1) * V7FS_BSIZE, &newmap);
+
+               if (oldmap.level != newmap.level) {
+                       switch (newmap.level) {
+                       case 0: /*1->0 */
+                               DPRINTF("1->0\n");
+                               idxblk = inode->addr[V7FS_NADDR_INDEX1];
+                               inode->addr[V7FS_NADDR_INDEX1] = 0;
+                               error = v7fs_datablock_deallocate(fs,
+                                   v7fs_remove_self(fs, idxblk));
+                               break;
+                       case 1: /*2->1 */
+                               DPRINTF("2->1\n");
+                               idxblk = inode->addr[V7FS_NADDR_INDEX2];
+                               inode->addr[V7FS_NADDR_INDEX2] = 0;
+                               error = v7fs_datablock_deallocate(fs,
+                                   v7fs_remove_self(fs, v7fs_remove_self(fs,
+                                   idxblk)));
+                               break;
+                       case 2:/*3->2 */
+                               DPRINTF("3->2\n");
+                               idxblk = inode->addr[V7FS_NADDR_INDEX3];
+                               inode->addr[V7FS_NADDR_INDEX3] = 0;
+                               error = v7fs_datablock_deallocate(fs,
+                                   v7fs_remove_self(fs, v7fs_remove_self(fs,
+                                       v7fs_remove_self(fs, idxblk))));
+                               break;
+                       }
+               } else {
+                       switch (newmap.level) {
+                       case 0:
+                               DPRINTF("[0] %d\n", oldmap.index[0]);
+                               blk = inode->addr[oldmap.index[0]];
+                               error = v7fs_datablock_deallocate(fs, blk);
+                               break;
+                       case 1:
+                               DPRINTF("[1] %d\n", oldmap.index[0]);
+                               idxblk = inode->addr[V7FS_NADDR_INDEX1];
+                               v7fs_remove_leaf(fs, idxblk, oldmap.index[0]);
+
+                               break;
+                       case 2:
+                               DPRINTF("[2] %d %d\n", oldmap.index[0],
+                                   oldmap.index[1]);
+                               idxblk = inode->addr[V7FS_NADDR_INDEX2];
+                               v7fs_remove_leaf(fs, v7fs_link(fs, idxblk,
+                                   oldmap.index[0]), oldmap.index[1]);
+                               if (oldmap.index[0] != newmap.index[0]) {
+                                       v7fs_remove_leaf(fs, idxblk,
+                                           oldmap.index[0]);
+                               }
+                               break;
+                       case 3:
+                               DPRINTF("[2] %d %d %d\n", oldmap.index[0],
+                                   oldmap.index[1], oldmap.index[2]);
+                               idxblk = inode->addr[V7FS_NADDR_INDEX3];
+                               v7fs_remove_leaf(fs, v7fs_link(fs,
+                                   v7fs_link(fs, idxblk, oldmap.index[0]),
+                                   oldmap.index[1]), oldmap.index[2]);
+
+                               if (oldmap.index[1] != newmap.index[1]) {
+                                       v7fs_remove_leaf(fs, v7fs_link(fs,
+                                           idxblk, oldmap.index[0]),
+                                           oldmap.index[1]);
+                               }
+                               if (oldmap.index[0] != newmap.index[0]) {
+                                       v7fs_remove_leaf(fs, idxblk,
+                                           oldmap.index[0]);
+                               }
+                               break;
+                       }
+               }
+               oldmap = newmap;
+       }
+       inode->filesize -= sz;
+       v7fs_inode_writeback(fs, inode);
+
+       return error;
+}
+
+static v7fs_daddr_t
+v7fs_unlink(struct v7fs_self *fs, v7fs_daddr_t idxblk, int n)
+{
+       v7fs_daddr_t *daddr_list;
+       v7fs_daddr_t blk;
+       void *buf;
+
+       if (!(buf = scratch_read(fs, idxblk)))
+               return 0;
+       daddr_list = (v7fs_daddr_t *)buf;
+       blk = V7FS_VAL32(fs, daddr_list[n]);
+       daddr_list[n] = 0;
+       fs->io.write(fs->io.cookie, buf, idxblk);
+       scratch_free(fs, buf);
+
+       return blk; /* unlinked block. */
+}
+
+static v7fs_daddr_t
+v7fs_remove_self(struct v7fs_self *fs, v7fs_daddr_t up)
+{
+       v7fs_daddr_t down;
+
+       if (!datablock_number_sanity(fs, up))
+               return 0;
+
+       /* At 1st, remove from datablock list. */
+       down = v7fs_unlink(fs, up, 0);
+
+       /* link self to freelist. */
+       v7fs_datablock_deallocate(fs, up);
+
+       return down;
+}
+
+static v7fs_daddr_t
+v7fs_remove_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int n)
+{
+       v7fs_daddr_t down;
+
+       if (!datablock_number_sanity(fs, up))
+               return 0;
+
+       /* At 1st, remove from datablock list. */
+       down = v7fs_unlink(fs, up, n);
+
+       /* link leaf to freelist. */
+       v7fs_datablock_deallocate(fs, down);
+
+       return down;
+}
+
+int
+v7fs_datablock_size_change(struct v7fs_self *fs, size_t newsz,
+    struct v7fs_inode *inode)
+{
+       ssize_t diff = newsz - v7fs_inode_filesize(inode);
+       int error = 0;
+
+       if (diff > 0)
+               error = v7fs_datablock_expand(fs, inode, diff);
+       else if (diff < 0)
+               error = v7fs_datablock_contract(fs, inode, -diff);
+
+       return error;
+}
+
+#ifdef V7FS_DATABLOCK_DEBUG
+void
+daddr_map_dump(const struct v7fs_daddr_map *map)
+{
+
+       DPRINTF("level %d ", map->level);
+       int m, n = !map->level ? 1 : map->level;
+       for (m = 0; m < n; m++)
+               printf("[%d]", map->index[m]);
+       printf("\n");
+}
+#endif
diff --git a/sys/fs/v7fs/v7fs_datablock.h b/sys/fs/v7fs/v7fs_datablock.h
new file mode 100644 (file)
index 0000000..42a45ad
--- /dev/null
@@ -0,0 +1,52 @@
+/*     $NetBSD: v7fs_datablock.h,v 1.2 2011/07/16 12:35:32 uch Exp $   */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _V7FS_DATABLOCK_H_
+#define        _V7FS_DATABLOCK_H_
+
+__BEGIN_DECLS
+bool datablock_number_sanity(const struct v7fs_self *, v7fs_daddr_t);
+int v7fs_datablock_allocate(struct v7fs_self *, v7fs_daddr_t *);
+int v7fs_datablock_foreach(struct v7fs_self *, struct v7fs_inode *,
+    int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
+v7fs_daddr_t v7fs_datablock_last(struct v7fs_self *, struct v7fs_inode *,
+    v7fs_off_t);
+int v7fs_datablock_expand(struct v7fs_self *, struct v7fs_inode *, size_t);
+int v7fs_datablock_contract(struct v7fs_self *, struct v7fs_inode *, size_t);
+int v7fs_datablock_size_change(struct v7fs_self *, size_t, struct v7fs_inode *);
+
+struct v7fs_daddr_map {
+       int level; /* direct, index1, index2, index3 */
+       v7fs_daddr_t index[3];
+};
+int v7fs_datablock_addr(size_t, struct v7fs_daddr_map *);
+__END_DECLS
+#endif /*!_V7FS_INODE_H_ */
diff --git a/sys/fs/v7fs/v7fs_dirent.c b/sys/fs/v7fs/v7fs_dirent.c
new file mode 100644 (file)
index 0000000..99431e7
--- /dev/null
@@ -0,0 +1,89 @@
+/*     $NetBSD: v7fs_dirent.c,v 1.2 2011/07/18 21:51:49 apb Exp $      */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_dirent.c,v 1.2 2011/07/18 21:51:49 apb Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#endif
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_endian.h"
+#include "v7fs_inode.h"
+#include "v7fs_dirent.h"
+
+#ifdef V7FS_DIRENT_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#endif
+
+bool
+v7fs_dirent_endian_convert(struct v7fs_self *fs, struct v7fs_dirent *dir, int n)
+{
+       struct v7fs_superblock *sb = &fs->superblock;
+       int i;
+       v7fs_ino_t ino;
+       bool ok = true;
+
+       for (i = 0; i < n; i++, dir++) {
+               ino = V7FS_VAL16(fs, dir->inode_number);
+               if (v7fs_inode_number_sanity(sb, ino)) {
+                       DPRINTF("Invalid inode# %d %s\n", ino, dir->name);
+                       ok = false;
+               }
+               dir->inode_number = ino;
+       }
+
+       return ok;
+}
+
+void
+v7fs_dirent_filename(char *dst/* size must be V7FS_NAME_MAX + 1 */,
+    const char *src)
+{
+
+       strncpy(dst, src, V7FS_NAME_MAX);
+       dst[V7FS_NAME_MAX] = '\0';
+}
diff --git a/sys/fs/v7fs/v7fs_dirent.h b/sys/fs/v7fs/v7fs_dirent.h
new file mode 100644 (file)
index 0000000..46ca28d
--- /dev/null
@@ -0,0 +1,38 @@
+/*     $NetBSD: v7fs_dirent.h,v 1.1 2011/06/27 11:52:24 uch Exp $      */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _V7FS_DIRENT_H_
+#define        _V7FS_DIRENT_H_
+__BEGIN_DECLS
+bool v7fs_dirent_endian_convert(struct v7fs_self *, struct v7fs_dirent *, int);
+void v7fs_dirent_filename(char *, const char *);
+__END_DECLS
+#endif /*!_V7FS_DIRENT_H_ */
diff --git a/sys/fs/v7fs/v7fs_endian.c b/sys/fs/v7fs/v7fs_endian.c
new file mode 100644 (file)
index 0000000..d3cd230
--- /dev/null
@@ -0,0 +1,231 @@
+/*     $NetBSD: v7fs_endian.c,v 1.2 2011/07/18 21:51:49 apb Exp $      */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_endian.c,v 1.2 2011/07/18 21:51:49 apb Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#include "v7fs.h"
+#include "v7fs_endian.h"
+#include "v7fs_impl.h"
+
+#ifndef BYTE_ORDER
+#error
+#endif
+
+/* PDP to Little */
+#define        bswap32pdp_le(x)                        \
+       ((uint32_t)                             \
+           ((((x) & 0xffff0000) >> 16) |       \
+               (((x) & 0x0000ffff) << 16)))
+/* PDP to Big */
+#define        bswap32pdp_be(x)                        \
+       ((uint32_t)                             \
+           ((((x) & 0xff00ff00) >> 8) |        \
+               (((x) & 0x00ff00ff) <<  8)))
+#ifdef V7FS_EI
+static uint32_t val32_normal_order(uint32_t);
+static uint32_t val32_reverse_order(uint32_t);
+#if BYTE_ORDER == LITTLE_ENDIAN
+static uint32_t val32_pdp_to_little(uint32_t);
+#else
+static uint32_t val32_pdp_to_big(uint32_t);
+#endif
+static uint16_t val16_normal_order(uint16_t);
+static uint16_t val16_reverse_order(uint16_t);
+static v7fs_daddr_t val24_reverse_order_read(uint8_t *);
+static void val24_reverse_order_write(v7fs_daddr_t, uint8_t *);
+static v7fs_daddr_t val24_pdp_read(uint8_t *);
+static void val24_pdp_write(v7fs_daddr_t, uint8_t *);
+
+static uint32_t
+val32_normal_order(uint32_t v)
+{
+
+       return v;
+}
+
+static uint32_t
+val32_reverse_order(uint32_t v)
+{
+
+       return bswap32(v);
+}
+#if BYTE_ORDER == LITTLE_ENDIAN
+static uint32_t
+val32_pdp_to_little(uint32_t v)
+{
+
+       return bswap32pdp_le(v);
+}
+#else
+static uint32_t
+val32_pdp_to_big(uint32_t v)
+{
+
+       return bswap32pdp_be(v);
+}
+#endif
+static uint16_t
+val16_normal_order(uint16_t v)
+{
+
+       return v;
+}
+
+static uint16_t
+val16_reverse_order(uint16_t v)
+{
+
+       return bswap16(v);
+}
+
+static v7fs_daddr_t
+val24_reverse_order_read(uint8_t *a)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+       return (a[0] << 16) | (a[1] << 8) | a[2];
+#else
+       return (a[2] << 16) | (a[1] << 8) | a[0];
+#endif
+}
+
+static void
+val24_reverse_order_write(v7fs_daddr_t addr, uint8_t *a)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+       a[0] = (addr >> 16) & 0xff;
+       a[1] = (addr >> 8) & 0xff;
+       a[2] = addr & 0xff;
+#else
+       a[0] = addr & 0xff;
+       a[1] = (addr >> 8) & 0xff;
+       a[2] = (addr >> 16) & 0xff;
+#endif
+}
+
+static v7fs_daddr_t
+val24_pdp_read(uint8_t *a)
+{
+
+       return (a[0] << 16) | a[1] | (a[2] << 8);
+}
+
+static void
+val24_pdp_write(v7fs_daddr_t addr, uint8_t *a)
+{
+
+       a[0] = (addr >> 16) & 0xff;
+       a[1] = addr & 0xff;
+       a[2] = (addr >> 8) & 0xff;
+}
+
+void
+v7fs_endian_init(struct v7fs_self *fs)
+{
+       struct endian_conversion_ops *ops = &fs->val;
+
+       switch (fs->endian)
+       {
+#if BYTE_ORDER == LITTLE_ENDIAN
+       case LITTLE_ENDIAN:
+               ops->conv32 = val32_normal_order;
+               ops->conv16 = val16_normal_order;
+               ops->conv24read = val24_normal_order_read;
+               ops->conv24write = val24_normal_order_write;
+               break;
+       case BIG_ENDIAN:
+               ops->conv32 = val32_reverse_order;
+               ops->conv16 = val16_reverse_order;
+               ops->conv24read = val24_reverse_order_read;
+               ops->conv24write = val24_reverse_order_write;
+               break;
+       case PDP_ENDIAN:
+               ops->conv32 = val32_pdp_to_little;
+               ops->conv16 = val16_normal_order;
+               ops->conv24read = val24_pdp_read;
+               ops->conv24write = val24_pdp_write;
+               break;
+#else /* BIG_ENDIAN */
+       case LITTLE_ENDIAN:
+               ops->conv32 = val32_reverse_order;
+               ops->conv16 = val16_reverse_order;
+               ops->conv24read = val24_reverse_order_read;
+               ops->conv24write = val24_reverse_order_write;
+               break;
+       case BIG_ENDIAN:
+               ops->conv32 = val32_normal_order;
+               ops->conv16 = val16_normal_order;
+               ops->conv24read = val24_normal_order_read;
+               ops->conv24write = val24_normal_order_write;
+               break;
+       case PDP_ENDIAN:
+               ops->conv32 = val32_pdp_to_big;
+               ops->conv16 = val16_reverse_order;
+               ops->conv24read = val24_pdp_read;
+               ops->conv24write = val24_pdp_write;
+               break;
+#endif
+       }
+}
+#endif /* V7FS_EI */
+v7fs_daddr_t
+val24_normal_order_read(uint8_t *a)
+{
+       /*(v7fs_daddr_t)cast is required for int 16bit system. */
+#if BYTE_ORDER == LITTLE_ENDIAN
+       return ((v7fs_daddr_t)a[2] << 16) | ((v7fs_daddr_t)a[1] << 8) |
+           (v7fs_daddr_t)a[0];
+#else
+       return ((v7fs_daddr_t)a[0] << 16) | ((v7fs_daddr_t)a[1] << 8) |
+           (v7fs_daddr_t)a[2];
+#endif
+}
+
+void
+val24_normal_order_write(v7fs_daddr_t addr, uint8_t *a)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+       a[0] = addr & 0xff;
+       a[1] = (addr >> 8) & 0xff;
+       a[2] = (addr >> 16) & 0xff;
+#else
+       a[0] = (addr >> 16) & 0xff;
+       a[1] = (addr >> 8) & 0xff;
+       a[2] = addr & 0xff;
+#endif
+}
diff --git a/sys/fs/v7fs/v7fs_endian.h b/sys/fs/v7fs/v7fs_endian.h
new file mode 100644 (file)
index 0000000..1d6411c
--- /dev/null
@@ -0,0 +1,52 @@
+/*     $NetBSD: v7fs_endian.h,v 1.1 2011/06/27 11:52:24 uch Exp $      */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _V7FS_ENDIAN_H_
+#define        _V7FS_ENDIAN_H_
+
+struct v7fs_self;
+__BEGIN_DECLS;
+v7fs_daddr_t val24_normal_order_read(uint8_t *);
+void val24_normal_order_write(v7fs_daddr_t, uint8_t *);
+__END_DECLS
+#ifdef V7FS_EI
+#define        V7FS_VAL32(x, v)        ((*(x)->val.conv32)(v))
+#define        V7FS_VAL16(x, v)        ((*(x)->val.conv16)(v))
+#define        V7FS_VAL24_READ(x, a)   ((*(x)->val.conv24read)(a))
+#define        V7FS_VAL24_WRITE(x, v, a) ((*(x)->val.conv24write)(v, a))
+void v7fs_endian_init(struct v7fs_self *);
+#else
+#define        V7FS_VAL32(x, v)        (v)
+#define        V7FS_VAL16(x, v)        (v)
+#define        V7FS_VAL24_READ(x, a)   (val24_normal_order_read(a))
+#define        V7FS_VAL24_WRITE(x, v, a) (val24_normal_order_write(v, a))
+#endif /*V7FS_EI */
+#endif /*!_V7FS_ENDIAN_H_ */
diff --git a/sys/fs/v7fs/v7fs_extern.c b/sys/fs/v7fs/v7fs_extern.c
new file mode 100644 (file)
index 0000000..7d4c737
--- /dev/null
@@ -0,0 +1,262 @@
+/*     $NetBSD: v7fs_extern.c,v 1.1 2011/06/27 11:52:24 uch Exp $      */
+
+/*-
+ * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_extern.c,v 1.1 2011/06/27 11:52:24 uch Exp $");
+
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+#include <sys/resource.h>
+#include <sys/param.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/module.h>
+
+#include <miscfs/fifofs/fifo.h>
+#include <miscfs/genfs/genfs.h>
+#include <miscfs/genfs/genfs_node.h>
+
+#include <fs/v7fs/v7fs_extern.h>
+
+MODULE(MODULE_CLASS_VFS, v7fs, NULL);
+
+/* External interfaces */
+
+int (**v7fs_vnodeop_p)(void *);        /* filled by getnewvnode (vnode.h) */
+
+const struct vnodeopv_entry_desc v7fs_vnodeop_entries[] = {
+       { &vop_default_desc, vn_default_error },
+       { &vop_lookup_desc, v7fs_lookup },              /* lookup */
+       { &vop_create_desc, v7fs_create },              /* create */
+       { &vop_mknod_desc, v7fs_mknod },                /* mknod */
+       { &vop_open_desc, v7fs_open },                  /* open */
+       { &vop_close_desc, v7fs_close },                /* close */
+       { &vop_access_desc, v7fs_access },              /* access */
+       { &vop_getattr_desc, v7fs_getattr },            /* getattr */
+       { &vop_setattr_desc, v7fs_setattr },            /* setattr */
+       { &vop_read_desc, v7fs_read },                  /* read */
+       { &vop_write_desc, v7fs_write },                /* write */
+       { &vop_fcntl_desc, genfs_fcntl },               /* fcntl */
+       { &vop_ioctl_desc, genfs_enoioctl },            /* ioctl */
+       { &vop_poll_desc, genfs_poll },                 /* poll */
+       { &vop_kqfilter_desc, genfs_kqfilter },         /* kqfilter */
+       { &vop_revoke_desc, genfs_revoke },             /* revoke */
+       { &vop_mmap_desc, genfs_mmap },                 /* mmap */
+       { &vop_fsync_desc, v7fs_fsync },                /* fsync */
+       { &vop_seek_desc, genfs_seek },                 /* seek */
+       { &vop_remove_desc, v7fs_remove },              /* remove */
+       { &vop_link_desc, v7fs_link },                  /* link */
+       { &vop_rename_desc, v7fs_rename },              /* rename */
+       { &vop_mkdir_desc, v7fs_mkdir },                /* mkdir */
+       { &vop_rmdir_desc, v7fs_rmdir },                /* rmdir */
+       { &vop_symlink_desc, v7fs_symlink },            /* symlink */
+       { &vop_readdir_desc, v7fs_readdir },            /* readdir */
+       { &vop_readlink_desc, v7fs_readlink },          /* readlink */
+       { &vop_abortop_desc, genfs_abortop },           /* abortop */
+       { &vop_inactive_desc, v7fs_inactive },          /* inactive */
+       { &vop_reclaim_desc, v7fs_reclaim },            /* reclaim */
+       { &vop_lock_desc, genfs_lock },                 /* lock */
+       { &vop_unlock_desc, genfs_unlock },             /* unlock */
+       { &vop_bmap_desc, v7fs_bmap },                  /* bmap */
+       { &vop_strategy_desc, v7fs_strategy },          /* strategy */
+       { &vop_print_desc, v7fs_print },                /* print */
+       { &vop_islocked_desc, genfs_islocked },         /* islocked */
+       { &vop_pathconf_desc, v7fs_pathconf },          /* pathconf */
+       { &vop_advlock_desc, v7fs_advlock },            /* advlock */
+       { &vop_bwrite_desc, vn_bwrite },                /* bwrite */
+       { &vop_getpages_desc, genfs_getpages },         /* getpages */
+       { &vop_putpages_desc, genfs_putpages },         /* putpages */
+       { NULL, NULL }
+};
+
+
+int (**v7fs_specop_p)(void *);
+const struct vnodeopv_entry_desc v7fs_specop_entries[] = {
+       { &vop_default_desc, vn_default_error },
+       { &vop_lookup_desc, spec_lookup },              /* lookup */
+       { &vop_create_desc, spec_create },              /* create xxx*/
+       { &vop_mknod_desc, spec_mknod },                /* mknod xxx*/
+       { &vop_open_desc, spec_open },                  /* open */
+       { &vop_close_desc, spec_close },                /* close */
+       { &vop_access_desc, v7fs_access },              /* access */
+       { &vop_getattr_desc, v7fs_getattr },            /* getattr */
+       { &vop_setattr_desc, v7fs_setattr },            /* setattr */
+       { &vop_read_desc, spec_read },                  /* read */
+       { &vop_write_desc, spec_write },                /* write */
+       { &vop_ioctl_desc, spec_ioctl },                /* ioctl */
+       { &vop_fcntl_desc, genfs_fcntl },               /* fcntl */
+       { &vop_poll_desc, spec_poll },                  /* poll */
+       { &vop_kqfilter_desc, spec_kqfilter },          /* kqfilter */
+       { &vop_revoke_desc, spec_revoke },              /* revoke */
+       { &vop_mmap_desc, spec_mmap },                  /* mmap */
+       { &vop_fsync_desc, spec_fsync },                /* fsync */
+       { &vop_seek_desc, spec_seek },                  /* seek */
+       { &vop_remove_desc, spec_remove },              /* remove */
+       { &vop_link_desc, spec_link },                  /* link */
+       { &vop_rename_desc, spec_rename },              /* rename */
+       { &vop_mkdir_desc, spec_mkdir },                /* mkdir */
+       { &vop_rmdir_desc, spec_rmdir },                /* rmdir */
+       { &vop_symlink_desc, spec_symlink },            /* symlink */
+       { &vop_readdir_desc, spec_readdir },            /* readdir */
+       { &vop_readlink_desc, spec_readlink },          /* readlink */
+       { &vop_abortop_desc, spec_abortop },            /* abortop */
+       { &vop_inactive_desc, v7fs_inactive },          /* inactive */
+       { &vop_reclaim_desc, v7fs_reclaim },            /* reclaim */
+       { &vop_lock_desc, genfs_lock },                 /* lock */
+       { &vop_unlock_desc, genfs_unlock },             /* unlock */
+       { &vop_bmap_desc, spec_bmap },                  /* bmap */
+       { &vop_strategy_desc, spec_strategy },          /* strategy */
+       { &vop_print_desc, spec_print },                /* print */
+       { &vop_islocked_desc, genfs_islocked },         /* islocked */
+       { &vop_pathconf_desc, spec_pathconf },          /* pathconf */
+       { &vop_advlock_desc, spec_advlock },            /* advlock */
+       { &vop_bwrite_desc, vn_bwrite },                /* bwrite */
+       { &vop_getpages_desc, spec_getpages },          /* getpages */
+       { &vop_putpages_desc, spec_putpages },          /* putpages */
+       { NULL, NULL }
+};
+
+int (**v7fs_fifoop_p)(void *);
+const struct vnodeopv_entry_desc v7fs_fifoop_entries[] = {
+       { &vop_default_desc, vn_default_error },
+       { &vop_lookup_desc, vn_fifo_bypass },           /* lookup */
+       { &vop_create_desc, vn_fifo_bypass },           /* create */
+       { &vop_mknod_desc, vn_fifo_bypass },            /* mknod */
+       { &vop_open_desc, vn_fifo_bypass },             /* open */
+       { &vop_close_desc, vn_fifo_bypass },            /* close */
+       { &vop_access_desc, v7fs_access },              /* access */
+       { &vop_getattr_desc, v7fs_getattr },            /* getattr */
+       { &vop_setattr_desc, v7fs_setattr },            /* setattr */
+       { &vop_read_desc, vn_fifo_bypass },             /* read */
+       { &vop_write_desc, vn_fifo_bypass },            /* write */
+       { &vop_ioctl_desc, vn_fifo_bypass },            /* ioctl */
+       { &vop_fcntl_desc, genfs_fcntl },               /* fcntl */
+       { &vop_poll_desc, vn_fifo_bypass },             /* poll */
+       { &vop_kqfilter_desc, vn_fifo_bypass },         /* kqfilter */
+       { &vop_revoke_desc, vn_fifo_bypass },           /* revoke */
+       { &vop_mmap_desc, vn_fifo_bypass },             /* mmap */
+       { &vop_fsync_desc, vn_fifo_bypass },            /* fsync */
+       { &vop_seek_desc, vn_fifo_bypass },             /* seek */
+       { &vop_remove_desc, vn_fifo_bypass },           /* remove */
+       { &vop_link_desc, vn_fifo_bypass },             /* link */
+       { &vop_rename_desc, vn_fifo_bypass },           /* rename */
+       { &vop_mkdir_desc, vn_fifo_bypass },            /* mkdir */
+       { &vop_rmdir_desc, vn_fifo_bypass },            /* rmdir */
+       { &vop_symlink_desc, vn_fifo_bypass },          /* symlink */
+       { &vop_readdir_desc, vn_fifo_bypass },          /* readdir */
+       { &vop_readlink_desc, vn_fifo_bypass },         /* readlink */
+       { &vop_abortop_desc, vn_fifo_bypass },          /* abortop */
+       { &vop_inactive_desc, v7fs_inactive },          /* inactive */
+       { &vop_reclaim_desc, v7fs_reclaim },            /* reclaim */
+       { &vop_lock_desc, vn_fifo_bypass },             /* lock */
+       { &vop_unlock_desc, vn_fifo_bypass },           /* unlock */
+       { &vop_bmap_desc, vn_fifo_bypass },             /* bmap */
+       { &vop_strategy_desc, vn_fifo_bypass },         /* strategy */
+       { &vop_print_desc, vn_fifo_bypass },            /* print */
+       { &vop_islocked_desc, vn_fifo_bypass },         /* islocked */
+       { &vop_pathconf_desc, vn_fifo_bypass },         /* pathconf */
+       { &vop_advlock_desc, vn_fifo_bypass },          /* advlock */
+       { &vop_bwrite_desc, vn_bwrite },                /* bwrite */
+       { &vop_putpages_desc, vn_fifo_bypass },         /* putpages */
+       { NULL, NULL }
+};
+
+const struct vnodeopv_desc v7fs_fifoop_opv_desc = {
+       &v7fs_fifoop_p,
+       v7fs_fifoop_entries
+};
+
+const struct vnodeopv_desc v7fs_vnodeop_opv_desc = {
+       &v7fs_vnodeop_p,
+       v7fs_vnodeop_entries
+};
+
+const struct vnodeopv_desc v7fs_specop_opv_desc = {
+       &v7fs_specop_p,
+       v7fs_specop_entries
+};
+
+const struct vnodeopv_desc *v7fs_vnodeopv_descs[] = {
+       &v7fs_vnodeop_opv_desc,
+       &v7fs_specop_opv_desc,
+       &v7fs_fifoop_opv_desc,
+       NULL,
+};
+
+const struct genfs_ops v7fs_genfsops = {
+       .gop_size = genfs_size,
+       .gop_alloc = v7fs_gop_alloc,
+       .gop_write = genfs_gop_write,
+};
+
+struct vfsops v7fs_vfsops = {
+       MOUNT_V7FS,
+       sizeof(struct v7fs_args),
+       v7fs_mount,
+       v7fs_start,
+       v7fs_unmount,
+       v7fs_root,
+       (void *)eopnotsupp,     /* vfs_quotactl */
+       v7fs_statvfs,
+       v7fs_sync,
+       v7fs_vget,
+       v7fs_fhtovp,
+       v7fs_vptofh,
+       v7fs_init,
+       v7fs_reinit,
+       v7fs_done,
+       v7fs_mountroot,
+       (int (*)(struct mount *, struct vnode *, struct timespec *))
+       eopnotsupp,             /* snapshot */
+       vfs_stdextattrctl,
+       (void *)eopnotsupp,     /* vfs_suspendctl */
+       genfs_renamelock_enter,
+       genfs_renamelock_exit,
+       (void *)eopnotsupp,
+       v7fs_vnodeopv_descs,
+       0,
+       { NULL, NULL }
+};
+
+static int
+v7fs_modcmd(modcmd_t cmd, void *arg)
+{
+
+       switch (cmd) {
+       case MODULE_CMD_INIT:
+               return vfs_attach(&v7fs_vfsops);
+       case MODULE_CMD_FINI:
+               return vfs_detach(&v7fs_vfsops);
+       default:
+               return ENOTTY;
+       }
+}
diff --git a/sys/fs/v7fs/v7fs_extern.h b/sys/fs/v7fs/v7fs_extern.h
new file mode 100644 (file)
index 0000000..80c4f9c
--- /dev/null
@@ -0,0 +1,116 @@
+/*     $NetBSD: v7fs_extern.h,v 1.1 2011/06/27 11:52:24 uch Exp $      */
+
+/*-
+ * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _FS_V7FS_EXTERN_H_
+#define        _FS_V7FS_EXTERN_H_
+
+#include <fs/v7fs/v7fs_args.h>
+
+#include <miscfs/genfs/genfs.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <miscfs/specfs/specdev.h>
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_inode.h"
+
+struct v7fs_mount {
+       struct mount *mountp;
+       struct vnode *devvp;            /* block device mounted vnode */
+       struct v7fs_self *core;         /* filesystem dependent implementation*/
+       LIST_HEAD(, v7fs_node) v7fs_node_head;
+};
+
+struct v7fs_node {
+       struct genfs_node gnode;
+       struct v7fs_inode inode; /* filesystem dependent implementation */
+       struct vnode *vnode;            /* back-link */
+       struct v7fs_mount *v7fsmount;   /* our filesystem */
+       struct lockf *lockf;            /* advlock */
+
+       int update_ctime;
+       int update_atime;
+       int update_mtime;
+
+       LIST_ENTRY(v7fs_node) link;
+};
+
+#define        VFSTOV7FS(mp)   ((struct v7fs_mount *)((mp)->mnt_data))
+
+__BEGIN_DECLS
+/* v-node ops. */
+int v7fs_lookup(void *);
+int v7fs_create(void *);
+int v7fs_open(void *);
+int v7fs_close(void *);
+int v7fs_access(void *);
+int v7fs_getattr(void *);
+int v7fs_setattr(void *);
+int v7fs_read(void *);
+int v7fs_write(void *);
+int v7fs_fsync(void *);
+int v7fs_remove(void *);
+int v7fs_rename(void *);
+int v7fs_readdir(void *);
+int v7fs_inactive(void *);
+int v7fs_reclaim(void *);
+int v7fs_bmap(void *);
+int v7fs_strategy(void *);
+int v7fs_print(void *);
+int v7fs_advlock(void *);
+int v7fs_pathconf(void *);
+
+int v7fs_link(void *);
+int v7fs_symlink(void *);
+int v7fs_readlink(void *);
+
+int v7fs_mkdir(void *);
+int v7fs_rmdir(void *);
+
+int v7fs_mknod(void *);
+
+/* vfs ops. */
+VFS_PROTOS(v7fs);
+
+int v7fs_mountroot(void);
+extern int (**v7fs_vnodeop_p)(void *);
+extern int (**v7fs_specop_p)(void *);
+extern int (**v7fs_fifoop_p)(void *);
+
+/* genfs ops */
+int v7fs_gop_alloc(struct vnode *, off_t, off_t, int, kauth_cred_t);
+extern const struct genfs_ops v7fs_genfsops;
+
+/* internal service */
+int v7fs_update(struct vnode *, const struct timespec *,
+    const struct timespec *, int);
+__END_DECLS
+#endif /* _FS_V7FS_EXTERN_H_ */
diff --git a/sys/fs/v7fs/v7fs_file.c b/sys/fs/v7fs/v7fs_file.c
new file mode 100644 (file)
index 0000000..f0d32b0
--- /dev/null
@@ -0,0 +1,422 @@
+/*     $NetBSD: v7fs_file.c,v 1.5 2012/12/07 06:50:15 msaitoh Exp $    */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_file.c,v 1.5 2012/12/07 06:50:15 msaitoh Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#include <sys/param.h>
+#ifdef _KERNEL
+#include <sys/systm.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#endif
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_endian.h"
+#include "v7fs_inode.h"
+#include "v7fs_dirent.h"
+#include "v7fs_file.h"
+#include "v7fs_datablock.h"
+
+#ifdef V7FS_FILE_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#endif
+
+static int lookup_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t);
+static int remove_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t);
+
+int
+v7fs_file_lookup_by_name(struct v7fs_self *fs, struct v7fs_inode *parent_dir,
+    const char *name, v7fs_ino_t *ino)
+{
+       char filename[V7FS_NAME_MAX + 1];
+       char *q;
+       int error;
+       size_t len;
+
+       if ((q = strchr(name, '/'))) {
+               /* Zap following path. */
+               len = MIN(V7FS_NAME_MAX, q - name);
+               memcpy(filename, name, len);
+               filename[len] = '\0';   /* '/' -> '\0' */
+       } else {
+               v7fs_dirent_filename(filename, name);
+       }
+       DPRINTF("%s(%s) dir=%d\n", filename, name, parent_dir->inode_number);
+
+       struct v7fs_lookup_arg lookup_arg = { .name = filename,
+                                             .inode_number = 0 };
+       if ((error = v7fs_datablock_foreach(fs, parent_dir, lookup_subr,
+                   &lookup_arg)) != V7FS_ITERATOR_BREAK) {
+               DPRINTF("not found.\n");
+               return ENOENT;
+       }
+
+       *ino = lookup_arg.inode_number;
+       DPRINTF("done. ino=%d\n", *ino);
+
+       return 0;
+}
+
+static int
+lookup_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz)
+{
+       struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx;
+       struct v7fs_dirent *dir;
+       const char *name = p->name;
+       void *buf;
+       size_t i, n;
+       int ret = 0;
+
+       if (!(buf = scratch_read(fs, blk)))
+               return EIO;
+
+       dir = (struct v7fs_dirent *)buf;
+       n = sz / sizeof(*dir);
+       v7fs_dirent_endian_convert(fs, dir, n);
+
+       for (i = 0; i < n; i++, dir++) {
+               if (dir->inode_number < 1) {
+                       DPRINTF("*** bad inode #%d ***\n", dir->inode_number);
+                       continue;
+               }
+
+               if (strncmp((const char *)dir->name, name, V7FS_NAME_MAX) == 0)
+               {
+                       p->inode_number = dir->inode_number;
+                       ret =  V7FS_ITERATOR_BREAK; /* found */
+                       break;
+               }
+       }
+       scratch_free(fs, buf);
+
+       return ret;
+}
+
+int
+v7fs_file_allocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir,
+    const char *srcname, struct v7fs_fileattr *attr, v7fs_ino_t *ino)
+{
+       struct v7fs_inode inode;
+       char filename[V7FS_NAME_MAX + 1];
+       struct v7fs_dirent *dir;
+       int error;
+
+       /* Truncate filename. */
+       v7fs_dirent_filename(filename, srcname);
+       DPRINTF("%s(%s)\n", filename, srcname);
+
+       /* Check filename. */
+       if (v7fs_file_lookup_by_name(fs, parent_dir, filename, ino) == 0) {
+               DPRINTF("%s exists\n", filename);
+               return EEXIST;
+       }
+
+       /* Get new inode. */
+       if ((error = v7fs_inode_allocate(fs, ino)))
+               return error;
+
+       /* Set initial attribute. */
+       memset(&inode, 0, sizeof(inode));
+       inode.inode_number = *ino;
+       inode.mode = attr->mode;
+       inode.uid = attr->uid;
+       inode.gid = attr->gid;
+       if (attr->ctime)
+               inode.ctime = attr->ctime;
+       if (attr->mtime)
+               inode.mtime = attr->mtime;
+       if (attr->atime)
+               inode.atime = attr->atime;
+
+       switch (inode.mode & V7FS_IFMT) {
+       default:
+               DPRINTF("Can't allocate %o type.\n", inode.mode);
+               v7fs_inode_deallocate(fs, *ino);
+               return EINVAL;
+       case V7FS_IFCHR:
+               /* FALLTHROUGH */
+       case V7FS_IFBLK:
+               inode.nlink = 1;
+               inode.device = attr->device;
+               inode.addr[0] = inode.device;
+               break;
+       case V7FSBSD_IFFIFO:
+               /* FALLTHROUGH */
+       case V7FSBSD_IFSOCK:
+               /* FALLTHROUGH */
+       case V7FSBSD_IFLNK:
+               /* FALLTHROUGH */
+       case V7FS_IFREG:
+               inode.nlink = 1;
+               break;
+       case V7FS_IFDIR:
+               inode.nlink = 2;        /* . + .. */
+               if ((error = v7fs_datablock_expand(fs, &inode, sizeof(*dir) * 2
+                   ))) {
+                       v7fs_inode_deallocate(fs, *ino);
+                       return error;
+               }
+               v7fs_daddr_t blk = inode.addr[0];
+               void *buf;
+               if (!(buf = scratch_read(fs, blk))) {
+                       v7fs_inode_deallocate(fs, *ino);
+                       return EIO;
+               }
+               dir = (struct v7fs_dirent *)buf;
+               strcpy(dir[0].name, ".");
+               dir[0].inode_number = V7FS_VAL16(fs, *ino);
+               strcpy(dir[1].name, "..");
+               dir[1].inode_number = V7FS_VAL16(fs, parent_dir->inode_number);
+               if (!fs->io.write(fs->io.cookie, buf, blk)) {
+                       scratch_free(fs, buf);
+                       return EIO;
+               }
+               scratch_free(fs, buf);
+               break;
+       }
+
+       v7fs_inode_writeback(fs, &inode);
+
+       /* Link this inode to parent directory. */
+       if ((error = v7fs_directory_add_entry(fs, parent_dir, *ino, filename)))
+       {
+               DPRINTF("can't add dirent.\n");
+               return error;
+       }
+
+       return 0;
+}
+
+int
+v7fs_file_deallocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir,
+    const char *name)
+{
+       v7fs_ino_t ino;
+       struct v7fs_inode inode;
+       int error;
+
+       DPRINTF("%s\n", name);
+       if ((error = v7fs_file_lookup_by_name(fs, parent_dir, name, &ino))) {
+               DPRINTF("no such a file: %s\n", name);
+               return error;
+       }
+       DPRINTF("%s->#%d\n", name, ino);
+       if ((error = v7fs_inode_load(fs, &inode, ino)))
+               return error;
+
+       if (v7fs_inode_isdir(&inode)) {
+               char filename[V7FS_NAME_MAX + 1];
+               v7fs_dirent_filename(filename, name);
+               /* Check parent */
+               if (strncmp(filename, "..", V7FS_NAME_MAX) == 0) {
+                       DPRINTF("can not remove '..'\n");
+                       return EINVAL; /* t_vnops rename_dotdot */
+               }
+               /* Check empty */
+               if (v7fs_inode_filesize(&inode) !=
+                   sizeof(struct v7fs_dirent) * 2 /*"." + ".."*/) {
+                       DPRINTF("directory not empty.\n");
+                       return ENOTEMPTY;/* t_vnops dir_noempty, rename_dir(6)*/
+               }
+               inode.nlink = 0;        /* remove this. */
+       } else {
+               /* Decrement reference count. */
+               --inode.nlink;  /* regular file. */
+               DPRINTF("%s nlink=%d\n", name, inode.nlink);
+       }
+
+
+       if ((error = v7fs_directory_remove_entry(fs, parent_dir, name)))
+               return error;
+       DPRINTF("remove dirent\n");
+
+       if (inode.nlink == 0) {
+               v7fs_datablock_contract(fs, &inode, inode.filesize);
+               DPRINTF("remove datablock\n");
+               v7fs_inode_deallocate(fs, ino);
+               DPRINTF("remove inode\n");
+       } else {
+               v7fs_inode_writeback(fs, &inode);
+       }
+
+       return 0;
+}
+
+int
+v7fs_directory_add_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir,
+    v7fs_ino_t ino, const char *srcname)
+{
+       struct v7fs_inode inode;
+       struct v7fs_dirent *dir;
+       int error = 0;
+       v7fs_daddr_t blk;
+       void *buf;
+       char filename[V7FS_NAME_MAX + 1];
+
+       /* Truncate filename. */
+       v7fs_dirent_filename(filename, srcname);
+       DPRINTF("%s(%s) %d\n", filename, srcname, ino);
+
+       /* Target inode */
+       if ((error = v7fs_inode_load(fs, &inode, ino)))
+               return error;
+
+       /* Expand datablock. */
+       if ((error = v7fs_datablock_expand(fs, parent_dir, sizeof(*dir))))
+               return error;
+
+       /* Read last entry. */
+       if (!(blk = v7fs_datablock_last(fs, parent_dir,
+           v7fs_inode_filesize(parent_dir))))
+               return EIO;
+
+       /* Load dirent block. This vnode(parent dir) is locked by VFS layer. */
+       if (!(buf = scratch_read(fs, blk)))
+               return EIO;
+
+       size_t sz = v7fs_inode_filesize(parent_dir);
+       sz = V7FS_RESIDUE_BSIZE(sz);    /* last block payload. */
+       int n = sz / sizeof(*dir) - 1;
+       /* Add dirent. */
+       dir = (struct v7fs_dirent *)buf;
+       dir[n].inode_number = V7FS_VAL16(fs, ino);
+       memcpy((char *)dir[n].name, filename, V7FS_NAME_MAX);
+       /* Write back datablock */
+       if (!fs->io.write(fs->io.cookie, buf, blk))
+               error = EIO;
+       scratch_free(fs, buf);
+
+       if (v7fs_inode_isdir(&inode)) {
+               parent_dir->nlink++;
+               v7fs_inode_writeback(fs, parent_dir);
+       }
+
+       DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize);
+
+       return error;
+}
+
+int
+v7fs_directory_remove_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir,
+    const char *name)
+{
+       struct v7fs_inode inode;
+       int error;
+       struct v7fs_dirent lastdirent;
+       v7fs_daddr_t lastblk;
+       size_t sz, lastsz;
+       v7fs_off_t pos;
+       void *buf;
+
+       /* Setup replaced entry. */
+       sz = parent_dir->filesize;
+       lastblk = v7fs_datablock_last(fs, parent_dir,
+           v7fs_inode_filesize(parent_dir));
+       lastsz = V7FS_RESIDUE_BSIZE(sz);
+       pos = lastsz - sizeof(lastdirent);
+
+       if (!(buf = scratch_read(fs, lastblk)))
+               return EIO;
+       lastdirent = *((struct v7fs_dirent *)((uint8_t *)buf + pos));
+       scratch_free(fs, buf);
+       DPRINTF("last dirent=%d %s pos=%d\n",
+           V7FS_VAL16(fs, lastdirent.inode_number), lastdirent.name, pos);
+
+       struct v7fs_lookup_arg lookup_arg =
+           { .name = name, .replace = &lastdirent/*disk endian */ };
+       /* Search entry that removed. replace it to last dirent. */
+       if ((error = v7fs_datablock_foreach(fs, parent_dir, remove_subr,
+           &lookup_arg)) != V7FS_ITERATOR_BREAK)
+               return ENOENT;
+
+       /* Contract dirent entries. */
+       v7fs_datablock_contract(fs, parent_dir, sizeof(lastdirent));
+       DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize);
+
+       /* Target inode */
+       if ((error = v7fs_inode_load(fs, &inode, lookup_arg.inode_number)))
+               return error;
+
+       if (v7fs_inode_isdir(&inode)) {
+               parent_dir->nlink--;
+               v7fs_inode_writeback(fs, parent_dir);
+       }
+
+       return 0;
+}
+
+static int
+remove_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz)
+{
+       struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx;
+       struct v7fs_dirent *dir;
+       void *buf;
+       size_t i;
+       int ret = 0;
+
+       DPRINTF("match start blk=%x\n", blk);
+       if (!(buf = scratch_read(fs, blk)))
+               return EIO;
+
+       dir = (struct v7fs_dirent *)buf;
+
+       for (i = 0; i < sz / sizeof(*dir); i++, dir++) {
+               DPRINTF("%d\n", V7FS_VAL16(fs, dir->inode_number));
+               if (strncmp(p->name,
+                       (const char *)dir->name, V7FS_NAME_MAX) == 0) {
+                       p->inode_number = V7FS_VAL16(fs, dir->inode_number);
+                       /* Replace to last dirent. */
+                       *dir = *(p->replace); /* disk endian */
+                       /* Write back. */
+                       if (!fs->io.write(fs->io.cookie, buf, blk))
+                               ret = EIO;
+                       else
+                               ret = V7FS_ITERATOR_BREAK;
+                       break;
+               }
+       }
+       scratch_free(fs, buf);
+
+       return ret;
+}
diff --git a/sys/fs/v7fs/v7fs_file.h b/sys/fs/v7fs/v7fs_file.h
new file mode 100644 (file)
index 0000000..26c39fb
--- /dev/null
@@ -0,0 +1,65 @@
+/*     $NetBSD: v7fs_file.h,v 1.2 2011/07/16 12:35:40 uch Exp $        */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _V7FS_FILE_H_
+#define        _V7FS_FILE_H_
+
+struct v7fs_lookup_arg {
+       const char *name;
+       char *buf;
+       v7fs_ino_t inode_number;
+       struct v7fs_dirent *replace;
+};
+
+__BEGIN_DECLS
+/* core */
+int v7fs_file_lookup_by_name(struct v7fs_self *, struct v7fs_inode *,
+    const char*, v7fs_ino_t *);
+int v7fs_file_allocate(struct v7fs_self *, struct v7fs_inode *, const char *,
+    struct v7fs_fileattr *, v7fs_ino_t *);
+int v7fs_file_deallocate(struct v7fs_self *, struct v7fs_inode *, const char *);
+int v7fs_directory_add_entry(struct v7fs_self *,struct v7fs_inode *, v7fs_ino_t,
+    const char *);
+int v7fs_directory_remove_entry(struct v7fs_self *,struct v7fs_inode *,
+    const char *);
+
+/* util */
+int v7fs_file_rename(struct v7fs_self *, struct v7fs_inode *, const char *,
+    struct v7fs_inode *, const char *);
+int v7fs_directory_replace_entry(struct v7fs_self *, struct v7fs_inode *,
+    const char *, v7fs_ino_t);
+int v7fs_file_link(struct v7fs_self *, struct v7fs_inode *, struct v7fs_inode *,
+    const char *);
+bool v7fs_file_lookup_by_number(struct v7fs_self *, struct v7fs_inode *,
+    v7fs_ino_t, char *);
+int v7fs_file_symlink(struct v7fs_self *, struct v7fs_inode *, const char *);
+__END_DECLS
+#endif /*!_V7FS_INODE_H_ */
diff --git a/sys/fs/v7fs/v7fs_file_util.c b/sys/fs/v7fs/v7fs_file_util.c
new file mode 100644 (file)
index 0000000..a6cfb32
--- /dev/null
@@ -0,0 +1,344 @@
+/*     $NetBSD: v7fs_file_util.c,v 1.4 2011/07/30 03:52:04 uch Exp $   */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_file_util.c,v 1.4 2011/07/30 03:52:04 uch Exp $");
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#endif
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_endian.h"
+#include "v7fs_inode.h"
+#include "v7fs_dirent.h"
+#include "v7fs_file.h"
+#include "v7fs_datablock.h"
+
+#ifdef V7FS_FILE_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#endif
+
+static int replace_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t);
+static int lookup_by_number_subr(struct v7fs_self *, void *, v7fs_daddr_t,
+    size_t);
+static int can_dirmove(struct v7fs_self *, v7fs_ino_t, v7fs_ino_t);
+static int lookup_parent_from_dir_subr(struct v7fs_self *, void *,
+    v7fs_daddr_t, size_t);
+
+int
+v7fs_file_link(struct v7fs_self *fs, struct v7fs_inode *parent_dir,
+    struct v7fs_inode *p, const char *name)
+{
+       int error = 0;
+
+       DPRINTF("%d %d %s\n", parent_dir->inode_number, p->inode_number, name);
+       if ((error = v7fs_directory_add_entry(fs, parent_dir, p->inode_number,
+           name))) {
+               DPRINTF("can't add entry");
+               return error;
+       }
+       p->nlink++;
+       v7fs_inode_writeback(fs, p);
+
+       return 0;
+}
+
+int
+v7fs_file_symlink(struct v7fs_self *fs, struct v7fs_inode *p,
+    const char *target)
+{
+       int error;
+       size_t len = strlen(target) + 1;
+
+       if (len > V7FSBSD_MAXSYMLINKLEN) {/* limited target 512byte pathname */
+               DPRINTF("too long pathname.");
+               return ENAMETOOLONG;
+       }
+
+       if ((error = v7fs_datablock_expand(fs, p, len))) {
+               return error;
+       }
+
+       v7fs_daddr_t blk = p->addr[0];  /* 1block only.  */
+       void *buf;
+       if (!(buf = scratch_read(fs, blk))) {
+               return EIO;
+       }
+
+       strncpy(buf, target, V7FS_BSIZE);
+       if (!fs->io.write(fs->io.cookie, buf, blk)) {
+               scratch_free(fs, buf);
+               return EIO;
+       }
+       scratch_free(fs, buf);
+       v7fs_inode_writeback(fs, p);
+
+       return 0;
+}
+
+int
+v7fs_file_rename(struct v7fs_self *fs, struct v7fs_inode *parent_from,
+    const char *from, struct v7fs_inode *parent_to, const char *to)
+{
+       v7fs_ino_t from_ino, to_ino;
+       struct v7fs_inode inode;
+       int error;
+       bool dir_move;
+
+       /* Check source file */
+       if ((error = v7fs_file_lookup_by_name(fs, parent_from, from,
+           &from_ino))) {
+               DPRINTF("%s don't exists\n", from);
+               return error;
+       }
+       v7fs_inode_load(fs, &inode, from_ino);
+       dir_move = v7fs_inode_isdir(&inode);
+
+       /* Check target file */
+       error = v7fs_file_lookup_by_name(fs, parent_to, to, &to_ino);
+       if (error == 0) {       /* found */
+               DPRINTF("%s already exists\n", to);
+               if ((error = v7fs_file_deallocate(fs, parent_to, to))) {
+                       DPRINTF("%s can't remove %d\n", to, error);
+                       return error;
+               }
+       } else if (error != ENOENT) {
+               DPRINTF("error=%d\n", error);
+               return error;
+       }
+       /* Check directory hierarchy. t_vnops rename_dir(5) */
+       if (dir_move && (error = can_dirmove(fs, from_ino,
+           parent_to->inode_number))) {
+               DPRINTF("dst '%s' is child dir of '%s'. error=%d\n", to, from,
+                   error);
+               return error;
+       }
+
+       if ((error = v7fs_directory_add_entry(fs, parent_to, from_ino, to))) {
+               DPRINTF("can't add entry");
+               return error;
+       }
+
+       if ((error = v7fs_directory_remove_entry(fs, parent_from, from))) {
+               DPRINTF("can't remove entry");
+               return error;
+       }
+
+       if (dir_move && (parent_from != parent_to)) {
+               /* If directory move, update ".." */
+               if ((error = v7fs_directory_replace_entry(fs, &inode, "..",
+                           parent_to->inode_number))) {
+                       DPRINTF("can't replace parent dir");
+                       return error;
+               }
+               v7fs_inode_writeback(fs, &inode);
+       }
+
+       return 0;
+}
+
+
+int
+v7fs_directory_replace_entry(struct v7fs_self *fs,  struct v7fs_inode *self_dir,
+    const char *name, v7fs_ino_t ino)
+{
+       int error;
+
+       /* Search entry that replaced. replace it to new inode number. */
+       struct v7fs_lookup_arg lookup_arg = { .name = name,
+                                             .inode_number = ino };
+       if ((error = v7fs_datablock_foreach(fs, self_dir, replace_subr,
+           &lookup_arg)) != V7FS_ITERATOR_BREAK)
+               return ENOENT;
+
+       return 0;
+}
+
+static int
+replace_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz)
+{
+       struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx;
+       struct v7fs_dirent *dir;
+       void *buf;
+       size_t i, n;
+       int ret = 0;
+
+       DPRINTF("match start blk=%x\n", blk);
+       if (!(buf = scratch_read(fs, blk)))
+               return EIO;
+
+       dir = (struct v7fs_dirent *)buf;
+       n = sz / sizeof(*dir);
+
+       for (i = 0; i < n; i++, dir++) { /*disk endian */
+               if (strncmp(p->name, (const char *)dir->name, V7FS_NAME_MAX)
+                   == 0) {
+                       /* Replace inode# */
+                       dir->inode_number = V7FS_VAL16(fs, p->inode_number);
+                       /* Write back. */
+                       if (!fs->io.write(fs->io.cookie, buf, blk))
+                               ret = EIO;
+                       else
+                               ret = V7FS_ITERATOR_BREAK;
+                       break;
+               }
+       }
+       scratch_free(fs, buf);
+
+       return ret;
+}
+
+bool
+v7fs_file_lookup_by_number(struct v7fs_self *fs, struct v7fs_inode *parent_dir,
+    v7fs_ino_t ino, char *buf)
+{
+       int ret;
+
+       ret = v7fs_datablock_foreach(fs, parent_dir, lookup_by_number_subr,
+           &(struct v7fs_lookup_arg){ .inode_number = ino, .buf = buf });
+
+       return ret == V7FS_ITERATOR_BREAK;
+}
+
+static int
+lookup_by_number_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk,
+    size_t sz)
+{
+       struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx;
+       struct v7fs_dirent *dir;
+       void *buf;
+       size_t i, n;
+       int ret = 0;
+
+       if (!(buf = scratch_read(fs, blk)))
+               return EIO;
+
+       dir = (struct v7fs_dirent *)buf;
+       n = sz / sizeof(*dir);
+       v7fs_dirent_endian_convert(fs, dir, n);
+
+       for (i = 0; i < n; i++, dir++) {
+               if (dir->inode_number == p->inode_number) {
+                       if (p->buf)
+                               v7fs_dirent_filename(p->buf, dir->name);
+                       ret = V7FS_ITERATOR_BREAK;
+                       break;
+               }
+       }
+       scratch_free(fs, buf);
+
+       return ret;
+}
+
+struct lookup_parent_arg {
+       v7fs_ino_t parent_ino;
+};
+
+static int
+can_dirmove(struct v7fs_self *fs, v7fs_ino_t from_ino, v7fs_ino_t to_ino)
+{
+       struct v7fs_inode inode;
+       v7fs_ino_t parent;
+       int error;
+
+       /* Start dir. */
+       if ((error = v7fs_inode_load(fs, &inode, to_ino)))
+               return error;
+
+       if (!v7fs_inode_isdir(&inode))
+               return ENOTDIR;
+
+       /* Lookup the parent. */
+       do {
+               struct lookup_parent_arg arg;
+               /* Search parent dir */
+               arg.parent_ino = 0;
+               v7fs_datablock_foreach(fs, &inode, lookup_parent_from_dir_subr,
+                   &arg);
+               if ((parent = arg.parent_ino) == 0) {
+                       DPRINTF("***parent missing\n");
+                       return ENOENT;
+               }
+               /* Load parent dir */
+               if ((error = v7fs_inode_load(fs, &inode, parent)))
+                       return error;
+               if (parent == from_ino) {
+                       DPRINTF("#%d is child dir of #%d\n", to_ino, from_ino);
+                       return EINVAL;
+               }
+       } while (parent != V7FS_ROOT_INODE);
+
+       return 0;
+}
+
+static int
+lookup_parent_from_dir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk,
+    size_t sz)
+{
+       struct lookup_parent_arg *arg = (struct lookup_parent_arg *)ctx;
+       char name[V7FS_NAME_MAX + 1];
+       void *buf;
+       int ret = 0;
+
+       if (!(buf = scratch_read(fs, blk)))
+               return 0;
+       struct v7fs_dirent *dir = (struct v7fs_dirent *)buf;
+       size_t i, n = sz / sizeof(*dir);
+       if (!v7fs_dirent_endian_convert(fs, dir, n)) {
+               scratch_free(fs, buf);
+               return V7FS_ITERATOR_ERROR;
+       }
+
+       for (i = 0; i < n; i++, dir++) {
+               v7fs_dirent_filename(name, dir->name);
+               if (strncmp(dir->name, "..", V7FS_NAME_MAX) != 0)
+                       continue;
+
+               arg->parent_ino = dir->inode_number;
+               ret = V7FS_ITERATOR_BREAK;
+               break;
+       }
+
+       scratch_free(fs, buf);
+       return ret;
+}
diff --git a/sys/fs/v7fs/v7fs_impl.h b/sys/fs/v7fs/v7fs_impl.h
new file mode 100644 (file)
index 0000000..a6cf6a3
--- /dev/null
@@ -0,0 +1,154 @@
+/*     $NetBSD: v7fs_impl.h,v 1.1 2011/06/27 11:52:25 uch Exp $        */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/* V7FS implementation. */
+#ifndef _V7FS_IMPL_H_
+#define        _V7FS_IMPL_H_
+
+#ifndef _KERNEL
+#include <stdbool.h>
+#include <assert.h>
+#define        KDASSERT(x)     assert(x)
+#endif
+
+struct block_io_ops {
+       void *cookie;
+       bool (*drive)(void *, uint8_t);
+       bool (*read)(void *, uint8_t *, daddr_t);
+       bool (*read_n)(void *, uint8_t *, daddr_t, int);
+       bool (*write)(void *, uint8_t *, daddr_t);
+       bool (*write_n)(void *, uint8_t *, daddr_t, int);
+};
+
+#ifdef V7FS_EI
+struct endian_conversion_ops {
+       uint32_t (*conv32)(uint32_t);
+       uint16_t (*conv16)(uint16_t);
+       /* For daddr packing */
+       v7fs_daddr_t (*conv24read)(uint8_t *);
+       void (*conv24write)(v7fs_daddr_t, uint8_t *);
+};
+#endif
+#ifdef _KERNEL
+#define        V7FS_LOCK
+#endif
+#ifdef V7FS_LOCK
+struct lock_ops {
+       void *cookie;
+       void (*lock)(void*);
+       void (*unlock)(void *);
+};
+#define        SUPERB_LOCK(x)          ((x)->sb_lock.lock((x)->sb_lock.cookie))
+#define        SUPERB_UNLOCK(x)        ((x)->sb_lock.unlock((x)->sb_lock.cookie))
+#define        ILIST_LOCK(x)           ((x)->ilist_lock.lock((x)->ilist_lock.cookie))
+#define        ILIST_UNLOCK(x)         ((x)->ilist_lock.unlock((x)->ilist_lock.cookie))
+#define        MEM_LOCK(x)             ((x)->mem_lock.lock((x)->mem_lock.cookie))
+#define        MEM_UNLOCK(x)           ((x)->mem_lock.unlock((x)->mem_lock.cookie))
+#else /*V7FS_LOCK */
+#define        SUPERB_LOCK(x)          ((void)0)
+#define        SUPERB_UNLOCK(x)        ((void)0)
+#define        ILIST_LOCK(x)           ((void)0)
+#define        ILIST_UNLOCK(x)         ((void)0)
+#define        MEM_LOCK(x)             ((void)0)
+#define        MEM_UNLOCK(x)           ((void)0)
+#endif /*V7FS_LOCK */
+
+struct v7fs_stat {
+       int32_t total_blocks;
+       int32_t free_blocks;
+       int32_t total_inode;
+       int32_t free_inode;
+       int32_t total_files;
+};
+
+struct v7fs_fileattr {
+       int16_t uid;
+       int16_t gid;
+       v7fs_mode_t mode;
+       v7fs_dev_t device;
+       v7fs_time_t ctime;
+       v7fs_time_t mtime;
+       v7fs_time_t atime;
+};
+
+struct v7fs_self {
+#define        V7FS_SELF_NSCRATCH      3
+       uint8_t scratch[V7FS_SELF_NSCRATCH][V7FS_BSIZE];
+       int scratch_free;       /* free block bitmap. */
+       int scratch_remain;     /* for statistic */
+       struct block_io_ops io;
+#ifdef V7FS_EI
+       struct endian_conversion_ops val;
+#endif
+#ifdef V7FS_LOCK
+       /* in-core superblock access. (freeblock/freeinode) split? -uch */
+       struct lock_ops sb_lock;
+       struct lock_ops ilist_lock;     /* disk ilist access. */
+       struct lock_ops mem_lock;       /* work memory allocation lock. */
+#endif
+       struct v7fs_superblock superblock;
+       struct v7fs_stat stat;
+       int endian;
+};
+
+struct v7fs_mount_device {
+       union {
+               void *vnode;    /* NetBSD kernel */
+               int fd;         /* NetBSD newfs,fsck */
+               const char *filename;/* misc test */
+       } device;
+       daddr_t sectors;        /*total size in sector. */
+       int endian;
+};
+
+#define        V7FS_ITERATOR_BREAK     (-1)
+#define        V7FS_ITERATOR_END       (-2)
+#define        V7FS_ITERATOR_ERROR     (-3)
+__BEGIN_DECLS
+int v7fs_io_init(struct v7fs_self **, const struct v7fs_mount_device *, size_t);
+void v7fs_io_fini(struct v7fs_self *);
+void *scratch_read(struct v7fs_self *, daddr_t);
+void scratch_free(struct v7fs_self *, void *);
+int scratch_remain(const struct v7fs_self *);
+__END_DECLS
+
+#if 0
+#define        V7FS_IO_DEBUG
+#define        V7FS_SUPERBLOCK_DEBUG
+#define        V7FS_DATABLOCK_DEBUG
+#define        V7FS_INODE_DEBUG
+#define        V7FS_DIRENT_DEBUG
+#define        V7FS_FILE_DEBUG
+#define        V7FS_VFSOPS_DEBUG
+#define        V7FS_VNOPS_DEBUG
+#endif
+
+#endif /*!_V7FS_IMPL_H_ */
diff --git a/sys/fs/v7fs/v7fs_inode.c b/sys/fs/v7fs/v7fs_inode.c
new file mode 100644 (file)
index 0000000..c3e683f
--- /dev/null
@@ -0,0 +1,301 @@
+/*     $NetBSD: v7fs_inode.c,v 1.2 2011/07/18 21:51:49 apb Exp $       */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_inode.c,v 1.2 2011/07/18 21:51:49 apb Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#endif
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_endian.h"
+#include "v7fs_inode.h"
+#include "v7fs_superblock.h"
+
+#ifdef V7FS_INODE_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#endif
+
+static void v7fs_inode_setup_disk_image(const struct v7fs_self *,
+    struct v7fs_inode *, struct v7fs_inode_diskimage *);
+static int v7fs_inode_inquire_disk_location(const struct v7fs_self *,
+    v7fs_ino_t, v7fs_daddr_t *, v7fs_daddr_t *);
+#ifdef V7FS_INODE_DEBUG
+static int v7fs_inode_block_sanity(const struct v7fs_superblock *,
+    v7fs_daddr_t);
+
+static int
+v7fs_inode_block_sanity(const struct v7fs_superblock *sb, v7fs_daddr_t blk)
+{
+
+       if ((blk < V7FS_ILIST_SECTOR) || (blk >= sb->datablock_start_sector)) {
+               DPRINTF("invalid inode block#%d (%d-%d)\n", blk,
+                   V7FS_ILIST_SECTOR, sb->datablock_start_sector);
+               return ENOSPC;
+       }
+
+       return 0;
+}
+#endif /* V7FS_INODE_DEBUG */
+
+int
+v7fs_inode_number_sanity(const struct v7fs_superblock *sb, v7fs_ino_t ino)
+{
+
+       if (ino < V7FS_ROOT_INODE || ((size_t)ino >= V7FS_MAX_INODE(sb))) {
+               DPRINTF("invalid inode#%d (%d-%zu)\n", ino,
+                   V7FS_ROOT_INODE, V7FS_MAX_INODE(sb));
+               return ENOSPC;
+       }
+
+       return 0;
+}
+
+int
+v7fs_inode_allocate(struct v7fs_self *fs, v7fs_ino_t *ino)
+{
+       struct v7fs_superblock *sb = &fs->superblock;
+       v7fs_ino_t inode_number;
+       int error = ENOSPC;
+       *ino = 0;
+
+       SUPERB_LOCK(fs);
+       if (sb->total_freeinode == 0) {
+               DPRINTF("inode exhausted!(1)\n");
+               goto errexit;
+       }
+
+       /* If there is no free inode cache, update it. */
+       if (sb->nfreeinode <= 0 && (error = v7fs_freeinode_update(fs))) {
+               DPRINTF("inode exhausted!(2)\n");
+               goto errexit;
+       }
+       /* Get inode from superblock cache. */
+       KDASSERT(sb->nfreeinode <= V7FS_MAX_FREEINODE);
+       inode_number = sb->freeinode[--sb->nfreeinode];
+       sb->total_freeinode--;
+       sb->modified = 1;
+
+       if ((error = v7fs_inode_number_sanity(sb, inode_number))) {
+               DPRINTF("new inode#%d %d %d\n", inode_number, sb->nfreeinode,
+                   sb->total_freeinode);
+               DPRINTF("free inode list corupt\n");
+               goto errexit;
+       }
+       *ino = inode_number;
+
+errexit:
+       SUPERB_UNLOCK(fs);
+
+       return error;
+}
+
+void
+v7fs_inode_deallocate(struct v7fs_self *fs, v7fs_ino_t ino)
+{
+       struct v7fs_superblock *sb = &fs->superblock;
+       struct v7fs_inode inode;
+
+       memset(&inode, 0, sizeof(inode));
+       inode.inode_number = ino;
+       v7fs_inode_writeback(fs, &inode);
+
+       SUPERB_LOCK(fs);
+       if (sb->nfreeinode < V7FS_MAX_FREEINODE) {
+               /* link to freeinode list. */
+               sb->freeinode[sb->nfreeinode++] = ino;
+       }
+       /* If superblock inode cache is full, this inode charged by
+          v7fs_freeinode_update() later. */
+       sb->total_freeinode++;
+       sb->modified = true;
+       SUPERB_UNLOCK(fs);
+}
+
+void
+v7fs_inode_setup_memory_image(const struct v7fs_self *fs __unused,
+    struct v7fs_inode *mem, struct v7fs_inode_diskimage *disk)
+{
+#define        conv16(m)       (mem->m = V7FS_VAL16(fs, (disk->m)))
+#define        conv32(m)       (mem->m = V7FS_VAL32(fs, (disk->m)))
+       uint32_t addr;
+       int i;
+
+       memset(mem, 0, sizeof(*mem));
+       conv16(mode);
+       conv16(nlink);
+       conv16(uid);
+       conv16(gid);
+       conv32(filesize);
+       conv32(atime);
+       conv32(mtime);
+       conv32(ctime);
+
+       for (i = 0; i < V7FS_NADDR; i++) {
+               int j = i * 3; /* 3 byte each. (v7fs_daddr is 24bit) */
+               /* expand to 4byte with endian conversion. */
+               addr = V7FS_VAL24_READ(fs, &disk->addr[j]);
+               mem->addr[i] = addr;
+       }
+       mem->device = 0;
+       if (v7fs_inode_iscdev(mem) || v7fs_inode_isbdev(mem)) {
+               mem->device = mem->addr[0];
+       }
+
+#undef conv16
+#undef conv32
+}
+
+static void
+v7fs_inode_setup_disk_image(const struct v7fs_self *fs __unused,
+    struct v7fs_inode *mem, struct v7fs_inode_diskimage *disk)
+{
+#define        conv16(m)       (disk->m = V7FS_VAL16(fs, (mem->m)))
+#define        conv32(m)       (disk->m = V7FS_VAL32(fs, (mem->m)))
+
+       conv16(mode);
+       conv16(nlink);
+       conv16(uid);
+       conv16(gid);
+       conv32(filesize);
+       conv32(atime);
+       conv32(mtime);
+       conv32(ctime);
+
+       int i;
+       for (i = 0; i < V7FS_NADDR; i++) {
+               int j = i * 3; /* 3 byte each. */
+               V7FS_VAL24_WRITE(fs, mem->addr[i], disk->addr + j);
+       }
+#undef conv16
+#undef conv32
+}
+
+/* Load inode from disk. */
+int
+v7fs_inode_load(struct v7fs_self *fs, struct v7fs_inode *p, v7fs_ino_t n)
+{
+       v7fs_daddr_t blk, ofs;
+       struct v7fs_inode_diskimage *di;
+       void *buf;
+
+       if (v7fs_inode_inquire_disk_location(fs, n, &blk, &ofs) != 0)
+               return ENOENT;
+
+       ILIST_LOCK(fs);
+       if (!(buf = scratch_read(fs, blk))) {
+               ILIST_UNLOCK(fs);
+               return EIO;
+       }
+       ILIST_UNLOCK(fs);
+       di = (struct v7fs_inode_diskimage *)buf;
+
+       /* Decode disk address, convert endian. */
+       v7fs_inode_setup_memory_image(fs, p, di + ofs);
+       p->inode_number = n;
+
+       scratch_free(fs, buf);
+
+       return 0;
+}
+
+/* Write back inode to disk. */
+int
+v7fs_inode_writeback(struct v7fs_self *fs, struct v7fs_inode *mem)
+{
+       struct v7fs_inode_diskimage disk;
+       v7fs_ino_t ino = mem->inode_number;
+       v7fs_daddr_t blk;
+       v7fs_daddr_t ofs;
+       void *buf;
+       int error = 0;
+
+       if (v7fs_inode_inquire_disk_location(fs, ino, &blk, &ofs) != 0)
+               return ENOENT;
+
+       v7fs_inode_setup_disk_image(fs, mem, &disk);
+
+       ILIST_LOCK(fs);
+       if (!(buf = scratch_read(fs, blk))) {
+               ILIST_UNLOCK(fs);
+               return EIO;
+       }
+       struct v7fs_inode_diskimage *di = (struct v7fs_inode_diskimage *)buf;
+       di[ofs] = disk; /* structure copy; */
+       if (!fs->io.write(fs->io.cookie, buf, blk))
+               error = EIO;
+       ILIST_UNLOCK(fs);
+
+       scratch_free(fs, buf);
+
+       return error;
+}
+
+static int
+v7fs_inode_inquire_disk_location(const struct v7fs_self *fs
+    __unused, v7fs_ino_t n, v7fs_daddr_t *block,
+    v7fs_daddr_t *offset)
+{
+       v7fs_daddr_t ofs, blk;
+#ifdef V7FS_INODE_DEBUG
+       v7fs_inode_number_sanity(&fs->superblock, n);
+#endif
+       ofs = (n - 1/*inode start from 1*/) *
+           sizeof(struct v7fs_inode_diskimage);
+       blk = ofs >> V7FS_BSHIFT;
+
+       *block = blk + V7FS_ILIST_SECTOR;
+       *offset = (ofs - blk * V7FS_BSIZE) /
+           sizeof(struct v7fs_inode_diskimage);
+#ifdef V7FS_INODE_DEBUG
+       return v7fs_inode_block_sanity(&fs->superblock, *block);
+#else
+       return 0;
+#endif
+}
+
diff --git a/sys/fs/v7fs/v7fs_inode.h b/sys/fs/v7fs/v7fs_inode.h
new file mode 100644 (file)
index 0000000..7dc42db
--- /dev/null
@@ -0,0 +1,91 @@
+/*     $NetBSD: v7fs_inode.h,v 1.1 2011/06/27 11:52:25 uch Exp $       */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _V7FS_INODE_H_
+#define        _V7FS_INODE_H_
+
+/* Software implementation of inode. (memory image) */
+struct v7fs_inode {
+       v7fs_ino_t inode_number;        /* inode location */
+       /* attr */
+       uint16_t mode;
+       int16_t nlink;
+       int16_t uid;
+       int16_t gid;
+       v7fs_time_t atime;
+       v7fs_time_t mtime;
+       v7fs_time_t ctime;
+       /* open mode */
+       bool append_mode;
+
+       v7fs_dev_t device;      /* for special file.(cdev, bdev) */
+       /* payload. */
+       v7fs_off_t filesize;    /* size of file (byte) */
+       v7fs_daddr_t addr[V7FS_NADDR];  /* data block address list */
+};
+
+#define        v7fs_inode_filesize(i)  ((i)->filesize)
+#define        v7fs_inode_allocated(i) ((i)->mode)
+#define        v7fs_inode_nlink(i)     ((i)->nlink)
+/* V7 original */
+#define        v7fs_inode_isdir(i)     (((i)->mode & V7FS_IFMT) == V7FS_IFDIR)
+#define        v7fs_inode_isfile(i)    (((i)->mode & V7FS_IFMT) == V7FS_IFREG)
+#define        v7fs_inode_iscdev(i)    (((i)->mode & V7FS_IFMT) == V7FS_IFCHR)
+#define        v7fs_inode_isbdev(i)    (((i)->mode & V7FS_IFMT) == V7FS_IFBLK)
+/* 2BSD extension (implementation is different) */
+#define        v7fs_inode_islnk(i)     (((i)->mode & V7FS_IFMT) == V7FSBSD_IFLNK)
+#define        v7fs_inode_issock(i)    (((i)->mode & V7FS_IFMT) == V7FSBSD_IFSOCK)
+/* NetBSD Extension */
+#define        v7fs_inode_isfifo(i)    (((i)->mode & V7FS_IFMT) == V7FSBSD_IFFIFO)
+
+__BEGIN_DECLS
+/* Free inode access ops. */
+int v7fs_inode_allocate(struct v7fs_self *, v7fs_ino_t *);
+void v7fs_inode_deallocate(struct v7fs_self *, v7fs_ino_t);
+
+/* Disk I/O ops. */
+int v7fs_inode_load(struct v7fs_self *, struct v7fs_inode *, v7fs_ino_t);
+int v7fs_inode_writeback(struct v7fs_self *, struct v7fs_inode *);
+void v7fs_inode_setup_memory_image(const struct v7fs_self *,
+    struct v7fs_inode *, struct v7fs_inode_diskimage *);
+
+/* Check. */
+int v7fs_inode_number_sanity(const struct v7fs_superblock *, v7fs_ino_t);
+
+/* Util. */
+void v7fs_inode_chmod(struct v7fs_inode *, v7fs_mode_t);
+void v7fs_inode_dump(const struct v7fs_inode *);
+
+/* Loop over all inode in ilist. */
+int v7fs_ilist_foreach(struct v7fs_self *, int (*)(struct v7fs_self *, void *,
+       struct v7fs_inode *, v7fs_ino_t), void *);
+__END_DECLS
+#endif /*!_V7FS_INODE_H_ */
diff --git a/sys/fs/v7fs/v7fs_inode_util.c b/sys/fs/v7fs/v7fs_inode_util.c
new file mode 100644 (file)
index 0000000..94dfd7a
--- /dev/null
@@ -0,0 +1,128 @@
+/*     $NetBSD: v7fs_inode_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $  */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_inode_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <time.h>
+#endif
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_inode.h"
+
+#ifdef V7FS_INODE_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#endif
+
+void
+v7fs_inode_chmod(struct v7fs_inode *inode, v7fs_mode_t mode)
+{
+#define        V7FS_MODE_MASK  0xfff
+       DPRINTF("setattr %08o -> %08o\n", inode->mode, mode);
+
+       inode->mode &= ~V7FS_MODE_MASK;
+       inode->mode |= (mode & V7FS_MODE_MASK);
+       DPRINTF("setattr %08o -> %08o\n", inode->mode, mode);
+}
+
+void
+v7fs_inode_dump(const struct v7fs_inode *p)
+{
+       printf("nlink %d mode %06o  %d/%d %d bytes\n",
+           p->nlink, p->mode,
+           p->uid, p->gid, p->filesize);
+
+       printf("atime %d mtime %d ctime %d\n",
+           p->atime, p->mtime, p->ctime);
+#ifndef _KERNEL
+       time_t at = p->atime;
+       time_t mt = p->mtime;
+       time_t ct = p->ctime;
+       printf(" atime %s mtime %s ctime %s", ctime(&at), ctime(&mt),
+           ctime(&ct));
+#endif
+       if (v7fs_inode_iscdev(p) || v7fs_inode_isbdev(p)) {
+               printf("device:%d/%d\n", (p->device >> 8), p->device & 0xff);
+       }
+       printf("\n");
+}
+
+
+int
+v7fs_ilist_foreach
+(struct v7fs_self *fs,
+    int (*func)(struct v7fs_self *, void *, struct v7fs_inode *, v7fs_ino_t),
+    void *ctx)
+{
+       struct v7fs_superblock *sb = &fs->superblock;
+       size_t i, j, k;
+       int ret;
+
+       /* Loop over ilist. */
+       for (k = 1, i = V7FS_ILIST_SECTOR; i < sb->datablock_start_sector;
+           i++) {
+               struct v7fs_inode_diskimage *di;
+               struct v7fs_inode inode;
+               void *buf;
+
+               if (!(buf = scratch_read(fs, i))) {
+                       DPRINTF("block %zu I/O error.\n", i);
+                       k += V7FS_INODE_PER_BLOCK;
+                       continue;
+               }
+               di = (struct v7fs_inode_diskimage *)buf;
+               for (j = 0; j < V7FS_INODE_PER_BLOCK; j++, k++) {
+                       v7fs_inode_setup_memory_image(fs, &inode, di + j);
+                       inode.inode_number = k;
+                       if ((ret = func(fs, ctx, &inode, k))) {
+                               scratch_free(fs, buf);
+                               return ret;
+                       }
+               }
+               scratch_free(fs, buf);
+       }
+       return 0;
+}
diff --git a/sys/fs/v7fs/v7fs_io.c b/sys/fs/v7fs/v7fs_io.c
new file mode 100644 (file)
index 0000000..3b5cca4
--- /dev/null
@@ -0,0 +1,141 @@
+/*     $NetBSD: v7fs_io.c,v 1.3 2013/06/28 14:49:14 christos Exp $     */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_io.c,v 1.3 2013/06/28 14:49:14 christos Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#endif
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+
+#if defined _KERNEL
+#define        STATIC_BUFFER
+#endif
+
+#ifdef V7FS_IO_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#endif
+
+void *
+scratch_read(struct v7fs_self *fs, daddr_t blk)
+{
+#ifdef STATIC_BUFFER
+       int i;
+       MEM_LOCK(fs);
+       for (i = 0; i < V7FS_SELF_NSCRATCH; i++) {
+               if (fs->scratch_free & (1 << i)) {
+                       fs->scratch_free &= ~(1 << i);
+                       break;
+               }
+       }
+       if (i == V7FS_SELF_NSCRATCH) {
+               DPRINTF("No scratch area. increase V7FS_SELF_NSCRATCH\n");
+               assert(0);
+               MEM_UNLOCK(fs);
+               return NULL;
+       }
+
+       if (!fs->io.read(fs->io.cookie, fs->scratch[i], blk)) {
+               DPRINTF("*** I/O error block %ld\n", (long)blk);
+               fs->scratch_free |= (1 << i);
+               MEM_UNLOCK(fs);
+               return NULL;
+       }
+       MEM_UNLOCK(fs);
+       /* Statistic */
+       int n;
+       if ((n = scratch_remain(fs)) < fs->scratch_remain)
+               fs->scratch_remain = n;
+
+       return fs->scratch[i];
+#else
+       uint8_t *buf = malloc(V7FS_BSIZE);
+       if (!fs->io.read(fs->io.cookie, buf, blk)) {
+               DPRINTF("*** I/O error block %ld\n",(long)blk);
+               free(buf);
+               return NULL;
+       }
+       return buf;
+#endif
+}
+
+int
+scratch_remain(const struct v7fs_self *fs)
+{
+#ifdef STATIC_BUFFER
+       int nfree;
+       int i;
+       MEM_LOCK(fs);
+       for (i = 0, nfree = 0; i < V7FS_SELF_NSCRATCH; i++) {
+               if (fs->scratch_free & (1 << i)) {
+                       nfree++;
+               }
+       }
+       MEM_UNLOCK(fs);
+       return nfree;
+#else
+       return -1;
+#endif
+}
+
+void
+scratch_free(struct v7fs_self *fs __unused, void *p)
+{
+#ifdef STATIC_BUFFER
+       int i;
+       MEM_LOCK(fs);
+       for (i = 0; i < V7FS_SELF_NSCRATCH; i++)
+               if (fs->scratch[i] == p) {
+                       fs->scratch_free |= (1 << i);
+                       break;
+               }
+       MEM_UNLOCK(fs);
+       assert(i != V7FS_SELF_NSCRATCH);
+#else
+       free(p);
+#endif
+}
diff --git a/sys/fs/v7fs/v7fs_io_kern.c b/sys/fs/v7fs/v7fs_io_kern.c
new file mode 100644 (file)
index 0000000..0151dc6
--- /dev/null
@@ -0,0 +1,241 @@
+/*     $NetBSD: v7fs_io_kern.c,v 1.2 2013/11/20 23:44:23 rmind Exp $   */
+
+/*-
+ * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_io_kern.c,v 1.2 2013/11/20 23:44:23 rmind Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+#include <sys/cdefs.h>
+
+__KERNEL_RCSID(0, "$NetBSD: v7fs_io_kern.c,v 1.2 2013/11/20 23:44:23 rmind Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/kmem.h>
+#include <sys/kauth.h>
+#include <sys/mutex.h>
+
+#include <fs/v7fs/v7fs.h>
+#include "v7fs_endian.h"
+#include "v7fs_impl.h"
+
+#ifdef V7FS_IO_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#endif
+
+struct local_io {
+       struct vnode *vp;
+       kauth_cred_t cred;
+};
+
+static bool v7fs_os_read_n(void *, uint8_t *, daddr_t, int);
+static bool v7fs_os_read(void *, uint8_t *, daddr_t);
+static bool v7fs_os_write_n(void *, uint8_t *, daddr_t, int);
+static bool v7fs_os_write(void *, uint8_t *, daddr_t);
+static void v7fs_os_lock(void *);
+static void v7fs_os_unlock(void *);
+static bool lock_init(struct lock_ops *);
+
+int
+v7fs_io_init(struct v7fs_self **fs,
+    const struct v7fs_mount_device *mount_device, size_t block_size)
+{
+       struct vnode *vp = mount_device->device.vnode;
+       struct v7fs_self *p;
+       struct local_io *local;
+       int error = 0;
+
+       if ((p = kmem_zalloc(sizeof(*p), KM_SLEEP)) == NULL)
+               return ENOMEM;
+
+       p->scratch_free = -1;
+       p->scratch_remain = V7FS_SELF_NSCRATCH;
+
+       /* Endian */
+       p->endian = mount_device->endian;
+#ifdef V7FS_EI
+       v7fs_endian_init(p);
+#endif
+       /* IO */
+       if ((local = kmem_zalloc(sizeof(*local), KM_SLEEP)) == NULL) {
+               error = ENOMEM;
+               goto errexit;
+       }
+       p->io.read = v7fs_os_read;
+       p->io.read_n = v7fs_os_read_n;
+       p->io.write = v7fs_os_write;
+       p->io.write_n = v7fs_os_write_n;
+       p->scratch_free = -1; /* free all scratch buffer */
+
+       p->io.cookie = local;
+       local->vp = vp;
+       local->cred = NOCRED;   /* upper layer check cred. */
+
+       /*LOCK */
+       error = ENOMEM;
+       if (!lock_init(&p->sb_lock))
+               goto errexit;
+       if (!lock_init(&p->ilist_lock))
+               goto errexit;
+       if (!lock_init(&p->mem_lock))
+               goto errexit;
+       error = 0;
+
+       *fs = p;
+       return 0;
+
+errexit:
+       v7fs_io_fini(p);
+       return error;
+}
+
+static bool
+lock_init(struct lock_ops *ops)
+{
+       if ((ops->cookie = kmem_zalloc(sizeof(kmutex_t), KM_SLEEP)) == NULL) {
+               return false;
+       }
+       mutex_init(ops->cookie, MUTEX_DEFAULT, IPL_NONE);
+       ops->lock = v7fs_os_lock;
+       ops->unlock = v7fs_os_unlock;
+       return true;
+}
+
+void
+v7fs_io_fini(struct v7fs_self *fs)
+{
+       if (fs->io.cookie) {
+               kmem_free(fs->io.cookie, sizeof(struct local_io));
+       }
+       if (fs->sb_lock.cookie) {
+               mutex_destroy(fs->sb_lock.cookie);
+               kmem_free(fs->sb_lock.cookie, sizeof(kmutex_t));
+       }
+       if (fs->ilist_lock.cookie) {
+               mutex_destroy(fs->ilist_lock.cookie);
+               kmem_free(fs->ilist_lock.cookie, sizeof(kmutex_t));
+       }
+       if (fs->mem_lock.cookie) {
+               mutex_destroy(fs->mem_lock.cookie);
+               kmem_free(fs->mem_lock.cookie, sizeof(kmutex_t));
+       }
+       kmem_free(fs, sizeof(*fs));
+}
+
+static bool
+v7fs_os_read_n(void *self, uint8_t *buf, daddr_t block, int count)
+{
+       int i;
+
+       for (i = 0; i < count; i++) {
+               if (!v7fs_os_read(self, buf, block))
+                       return false;
+               buf += DEV_BSIZE;
+               block++;
+       }
+
+       return true;
+}
+
+static bool
+v7fs_os_read(void *self, uint8_t *buf, daddr_t block)
+{
+       struct local_io *bio = (struct local_io *)self;
+       struct buf *bp = NULL;
+
+       if (bread(bio->vp, block, DEV_BSIZE, bio->cred, 0, &bp) != 0)
+               goto error_exit;
+       memcpy(buf, bp->b_data, DEV_BSIZE);
+       brelse(bp, 0);
+
+       return true;
+error_exit:
+       DPRINTF("block %ld read failed.\n", (long)block);
+
+       if (bp != NULL)
+               brelse(bp, 0);
+       return false;
+}
+
+static bool
+v7fs_os_write_n(void *self, uint8_t *buf, daddr_t block, int count)
+{
+       int i;
+
+       for (i = 0; i < count; i++) {
+               if (!v7fs_os_write(self, buf, block))
+                       return false;
+               buf += DEV_BSIZE;
+               block++;
+       }
+
+       return true;
+}
+
+static bool
+v7fs_os_write(void *self, uint8_t *buf, daddr_t block)
+{
+       struct local_io *bio = (struct local_io *)self;
+       struct buf *bp;
+
+       if ((bp = getblk(bio->vp, block, DEV_BSIZE, 0, 0)) == 0) {
+               DPRINTF("getblk failed. block=%ld\n", (long)block);
+               return false;
+       }
+
+       memcpy(bp->b_data, buf, DEV_BSIZE);
+
+       if (bwrite(bp) != 0) {
+               DPRINTF("bwrite failed. block=%ld\n", (long)block);
+               return false;
+       }
+
+       return true;
+}
+
+static void
+v7fs_os_lock(void *self)
+{
+
+       mutex_enter((kmutex_t *)self);
+}
+
+static void
+v7fs_os_unlock(void *self)
+{
+
+       mutex_exit((kmutex_t *)self);
+}
diff --git a/sys/fs/v7fs/v7fs_io_user.c b/sys/fs/v7fs/v7fs_io_user.c
new file mode 100644 (file)
index 0000000..907a78b
--- /dev/null
@@ -0,0 +1,172 @@
+/*     $NetBSD: v7fs_io_user.c,v 1.4 2011/08/08 11:42:30 uch Exp $     */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: v7fs_io_user.c,v 1.4 2011/08/08 11:42:30 uch Exp $");
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <err.h>
+#include <sys/mman.h>
+#include "v7fs.h"
+#include "v7fs_endian.h"
+#include "v7fs_impl.h"
+
+#ifdef V7FS_IO_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#endif
+
+struct local_io {
+       int fd;
+       size_t size;
+       size_t blksz;
+       uint8_t *addr;
+} local;
+
+static bool read_sector(void *, uint8_t *, daddr_t);
+static bool write_sector(void *, uint8_t *, daddr_t);
+static bool read_mmap(void *, uint8_t *, daddr_t);
+static bool write_mmap(void *, uint8_t *, daddr_t);
+
+int
+v7fs_io_init(struct v7fs_self **fs, const struct v7fs_mount_device *mount,
+    size_t block_size)
+{
+       struct v7fs_self *p;
+
+       if (!(p = (struct v7fs_self *)malloc(sizeof(*p))))
+               return ENOMEM;
+       memset(p, 0, sizeof(*p));
+
+       /* Endian */
+       p->endian = mount->endian;
+#ifdef V7FS_EI
+       v7fs_endian_init(p);
+#endif
+       local.blksz = block_size;
+       local.fd = mount->device.fd;
+       local.size = mount->sectors * block_size;
+       local.addr = mmap(NULL, local.size, PROT_READ | PROT_WRITE | PROT_NONE,
+           MAP_FILE | MAP_SHARED/*writeback*/, local.fd,  0);
+       if (local.addr == MAP_FAILED) {
+               local.addr = 0;
+               p->io.read = read_sector;
+               p->io.write = write_sector;
+       } else {
+               DPRINTF("mmaped addr=%p\n", local.addr);
+               p->io.read = read_mmap;
+               p->io.write = write_mmap;
+       }
+
+       p->io.cookie = &local;
+       *fs = p;
+
+       return 0;
+}
+
+void
+v7fs_io_fini(struct v7fs_self *fs)
+{
+       struct local_io *lio = (struct local_io *)fs->io.cookie;
+
+       if (lio->addr) {
+               if (munmap(lio->addr, lio->size) != 0)
+                       warn(0);
+       }
+       fsync(lio->fd);
+
+       free(fs);
+}
+
+static bool
+read_sector(void *ctx, uint8_t *buf, daddr_t sector)
+{
+       struct local_io *lio = (struct local_io *)ctx;
+       size_t blksz = lio->blksz;
+       int fd = lio->fd;
+
+       if ((lseek(fd, (off_t)sector * blksz, SEEK_SET) < 0) ||
+           (read(fd, buf, blksz) < (ssize_t)blksz)) {
+               warn("sector=%ld\n", (long)sector);
+               return false;
+       }
+
+       return true;
+}
+
+static bool
+write_sector(void *ctx, uint8_t *buf, daddr_t sector)
+{
+       struct local_io *lio = (struct local_io *)ctx;
+       size_t blksz = lio->blksz;
+       int fd = lio->fd;
+
+       if ((lseek(fd, (off_t)sector * blksz, SEEK_SET) < 0) ||
+           (write(fd, buf, blksz) < (ssize_t)blksz)) {
+               warn("sector=%ld\n", (long)sector);
+               return false;
+       }
+
+       return true;
+}
+
+static bool
+read_mmap(void *ctx, uint8_t *buf, daddr_t sector)
+{
+       struct local_io *lio = (struct local_io *)ctx;
+       size_t blksz = lio->blksz;
+
+       memcpy(buf, lio->addr + sector * blksz, blksz);
+
+       return true;
+}
+
+static bool
+write_mmap(void *ctx, uint8_t *buf, daddr_t sector)
+{
+       struct local_io *lio = (struct local_io *)ctx;
+       size_t blksz = lio->blksz;
+
+       memcpy(lio->addr + sector * blksz, buf, blksz);
+
+       return true;
+}
diff --git a/sys/fs/v7fs/v7fs_superblock.c b/sys/fs/v7fs/v7fs_superblock.c
new file mode 100644 (file)
index 0000000..413e0fa
--- /dev/null
@@ -0,0 +1,263 @@
+/*     $NetBSD: v7fs_superblock.c,v 1.2 2011/07/18 21:51:49 apb Exp $  */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_superblock.c,v 1.2 2011/07/18 21:51:49 apb Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/param.h> /* errno */
+#else
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#endif
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_endian.h"
+#include "v7fs_superblock.h"
+#include "v7fs_inode.h"
+#include "v7fs_datablock.h"
+
+#ifdef V7FS_SUPERBLOCK_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#define        DPRINTF_(fmt, args...)  printf(fmt, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#define        DPRINTF_(fmt, args...)  ((void)0)
+#endif
+
+static void v7fs_superblock_endian_convert(struct v7fs_self *,
+    struct v7fs_superblock *, struct v7fs_superblock *);
+static int v7fs_superblock_sanity(struct v7fs_self *);
+
+/* Load superblock from disk. */
+int
+v7fs_superblock_load(struct v7fs_self *fs)
+{
+       struct v7fs_superblock *disksb;
+       void *buf;
+       int error;
+
+       if (!(buf = scratch_read(fs, V7FS_SUPERBLOCK_SECTOR)))
+               return EIO;
+       disksb = (struct v7fs_superblock *)buf;
+       v7fs_superblock_endian_convert(fs, &fs->superblock, disksb);
+       scratch_free(fs, buf);
+
+       if ((error = v7fs_superblock_sanity(fs)))
+               return error;
+
+       return 0;
+}
+
+/* Writeback superblock to disk. */
+int
+v7fs_superblock_writeback(struct v7fs_self *fs)
+{
+       struct v7fs_superblock *memsb = &fs->superblock;
+       struct v7fs_superblock *disksb;
+       void *buf;
+       int error = 0;
+
+       if (!memsb->modified)
+               return 0;
+
+       if (!(buf = scratch_read(fs, V7FS_SUPERBLOCK_SECTOR)))
+               return EIO;
+       disksb = (struct v7fs_superblock *)buf;
+       v7fs_superblock_endian_convert(fs, disksb, memsb);
+       if (!fs->io.write(fs->io.cookie, buf, V7FS_SUPERBLOCK_SECTOR))
+               error = EIO;
+       scratch_free(fs, buf);
+
+       memsb->modified = 0;
+       DPRINTF("done. %d\n", error);
+
+       return error;
+}
+
+/* Check endian mismatch. */
+static int
+v7fs_superblock_sanity(struct v7fs_self *fs)
+{
+       const struct v7fs_superblock *sb = &fs->superblock;
+       void *buf = 0;
+
+       if ((sb->volume_size < 128) || /* smaller than 64KB. */
+           (sb->datablock_start_sector > sb->volume_size) ||
+           (sb->nfreeinode > V7FS_MAX_FREEINODE) ||
+           (sb->nfreeblock > V7FS_MAX_FREEBLOCK) ||
+           (sb->update_time < 0) ||
+           (sb->total_freeblock > sb->volume_size) ||
+           ((sb->nfreeinode == 0) && (sb->nfreeblock == 0) &&
+               (sb->total_freeblock == 0) && (sb->total_freeinode == 0)) ||
+           (!(buf = scratch_read(fs, sb->volume_size - 1)))) {
+               DPRINTF("invalid super block.\n");
+               return EINVAL;
+       }
+       if (buf)
+               scratch_free(fs, buf);
+
+       return 0;
+}
+
+/* Fill free block to superblock cache. */
+int
+v7fs_freeblock_update(struct v7fs_self *fs, v7fs_daddr_t blk)
+{
+       /* Assume superblock is locked by caller. */
+       struct v7fs_superblock *sb = &fs->superblock;
+       struct v7fs_freeblock *fb;
+       void *buf;
+       int error;
+
+       /* Read next freeblock table from disk. */
+       if (!datablock_number_sanity(fs, blk) || !(buf = scratch_read(fs, blk)))
+               return EIO;
+
+       /* Update in-core superblock freelist. */
+       fb = (struct v7fs_freeblock *)buf;
+       if ((error = v7fs_freeblock_endian_convert(fs, fb))) {
+               scratch_free(fs, buf);
+               return error;
+       }
+       DPRINTF("freeblock table#%d, nfree=%d\n", blk, fb->nfreeblock);
+
+       memcpy(sb->freeblock, fb->freeblock, sizeof(blk) * fb->nfreeblock);
+       sb->nfreeblock = fb->nfreeblock;
+       sb->modified = true;
+       scratch_free(fs, buf);
+
+       return 0;
+}
+
+int
+v7fs_freeblock_endian_convert(struct v7fs_self *fs __unused,
+    struct v7fs_freeblock *fb __unused)
+{
+#ifdef V7FS_EI
+       int i;
+       int16_t nfree;
+
+       nfree = V7FS_VAL16(fs, fb->nfreeblock);
+       if (nfree <= 0 || nfree > V7FS_MAX_FREEBLOCK) {
+               DPRINTF("invalid freeblock list. %d (max=%d)\n", nfree,
+                   V7FS_MAX_FREEBLOCK);
+               return ENOSPC;
+       }
+       fb->nfreeblock = nfree;
+
+       for (i = 0; i < nfree; i++) {
+               fb->freeblock[i] = V7FS_VAL32(fs, fb->freeblock[i]);
+       }
+#endif /* V7FS_EI */
+
+       return 0;
+}
+
+/* Fill free inode to superblock cache. */
+int
+v7fs_freeinode_update(struct v7fs_self *fs)
+{
+       /* Assume superblock is locked by caller. */
+       struct v7fs_superblock *sb = &fs->superblock;
+       v7fs_ino_t *freeinode = sb->freeinode;
+       size_t i, j, k;
+       v7fs_ino_t ino;
+
+       /* Loop over all inode list. */
+       for (i = V7FS_ILIST_SECTOR, ino = 1/* inode start from 1*/, k = 0;
+           i < sb->datablock_start_sector; i++) {
+               struct v7fs_inode_diskimage *di;
+               void *buf;
+               if (!(buf = scratch_read(fs, i))) {
+                       DPRINTF("block %zu I/O error.\n", i);
+                       ino += V7FS_INODE_PER_BLOCK;
+                       continue;
+               }
+               di = (struct v7fs_inode_diskimage *)buf;
+
+               for (j = 0;
+                   (j < V7FS_INODE_PER_BLOCK) && (k < V7FS_MAX_FREEINODE);
+                   j++, di++, ino++) {
+                       if (v7fs_inode_allocated(di))
+                               continue;
+                       DPRINTF("free inode%d\n", ino);
+                       freeinode[k++] = ino;
+               }
+               scratch_free(fs, buf);
+       }
+       sb->nfreeinode = k;
+
+       return 0;
+}
+
+static void
+v7fs_superblock_endian_convert(struct v7fs_self *fs __unused,
+    struct v7fs_superblock *to,  struct v7fs_superblock *from)
+{
+#ifdef V7FS_EI
+#define        conv16(m)       (to->m = V7FS_VAL16(fs, from->m))
+#define        conv32(m)       (to->m = V7FS_VAL32(fs, from->m))
+       int i;
+
+       conv16(datablock_start_sector);
+       conv32(volume_size);
+       conv16(nfreeblock);
+       v7fs_daddr_t *dfrom = from->freeblock;
+       v7fs_daddr_t *dto = to->freeblock;
+       for (i = 0; i < V7FS_MAX_FREEBLOCK; i++, dfrom++, dto++)
+               *dto = V7FS_VAL32(fs, *dfrom);
+
+       conv16(nfreeinode);
+       v7fs_ino_t *ifrom = from->freeinode;
+       v7fs_ino_t *ito = to->freeinode;
+       for (i = 0; i < V7FS_MAX_FREEINODE; i++, ifrom++, ito++)
+               *ito = V7FS_VAL16(fs, *ifrom);
+
+       conv32(update_time);
+       conv32(total_freeblock);
+       conv16(total_freeinode);
+#undef conv16
+#undef conv32
+#else /* V7FS_EI */
+       memcpy(to, from , sizeof(*to));
+#endif /* V7FS_EI */
+}
diff --git a/sys/fs/v7fs/v7fs_superblock.h b/sys/fs/v7fs/v7fs_superblock.h
new file mode 100644 (file)
index 0000000..b58716c
--- /dev/null
@@ -0,0 +1,48 @@
+/*     $NetBSD: v7fs_superblock.h,v 1.1 2011/06/27 11:52:25 uch Exp $  */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _V7FS_SUPERBLOCK_H_
+#define        _V7FS_SUPERBLOCK_H_
+struct v7fs_self;
+
+__BEGIN_DECLS
+/* core */
+int v7fs_superblock_load(struct v7fs_self *);
+int v7fs_superblock_writeback(struct v7fs_self *);
+int v7fs_freeblock_update(struct v7fs_self *, v7fs_daddr_t);
+int v7fs_freeblock_endian_convert(struct v7fs_self *, struct v7fs_freeblock *);
+int v7fs_freeinode_update(struct v7fs_self *);
+
+/* util. */
+void v7fs_superblock_status(struct v7fs_self *);
+void v7fs_superblock_dump(const struct v7fs_self *);
+__END_DECLS
+#endif /*!_V7FS_SUPERBLOCK_H_ */
diff --git a/sys/fs/v7fs/v7fs_superblock_util.c b/sys/fs/v7fs/v7fs_superblock_util.c
new file mode 100644 (file)
index 0000000..27417ee
--- /dev/null
@@ -0,0 +1,101 @@
+/*     $NetBSD: v7fs_superblock_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $     */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_superblock_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/param.h> /* errno */
+#else
+#include <stdio.h>
+#include <time.h>
+#endif
+
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_superblock.h"
+#include "v7fs_inode.h"
+
+#ifdef V7FS_SUPERBLOCK_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#define        DPRINTF_(fmt, args...)  printf(fmt, ##args)
+#else
+#define        DPRINTF(fmt, args...)   ((void)0)
+#define        DPRINTF_(fmt, args...)  ((void)0)
+#endif
+
+void
+v7fs_superblock_status(struct v7fs_self *fs)
+{
+       struct v7fs_superblock *sb = &fs->superblock;
+       struct v7fs_stat *stat = &fs->stat;
+
+       stat->total_blocks = sb->volume_size - sb->datablock_start_sector;
+       stat->total_inode = V7FS_MAX_INODE(sb);
+       stat->free_inode = sb->total_freeinode;
+       stat->free_blocks = sb->total_freeblock;
+       stat->total_files = stat->total_inode - sb->total_freeinode - 1;
+
+       DPRINTF("block %d/%d, inode %d/%d\n", stat->free_blocks,
+           stat->total_blocks, stat->free_inode, stat->total_inode);
+}
+
+void
+v7fs_superblock_dump(const struct v7fs_self *fs)
+{
+       const struct v7fs_superblock *sb = &fs->superblock;
+
+#define        print(x)        printf("%s: %d\n", #x, sb->x)
+       print(datablock_start_sector);
+       print(volume_size);
+       print(nfreeblock);
+       print(nfreeinode);
+       print(update_time);
+       print(lock_freeblock);
+       print(lock_freeinode);
+       print(modified);
+       print(readonly);
+#if !defined _KERNEL
+       time_t t = sb->update_time;
+       printf("%s", ctime(&t));
+#endif
+       print(total_freeblock);
+       print(total_freeinode);
+#undef print
+}
diff --git a/sys/fs/v7fs/v7fs_vfsops.c b/sys/fs/v7fs/v7fs_vfsops.c
new file mode 100644 (file)
index 0000000..e8e95ee
--- /dev/null
@@ -0,0 +1,617 @@
+/*     $NetBSD: v7fs_vfsops.c,v 1.9 2013/11/23 13:35:36 christos Exp $ */
+
+/*-
+ * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_vfsops.c,v 1.9 2013/11/23 13:35:36 christos Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/pool.h>
+#include <sys/time.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/disk.h>
+#include <sys/device.h>
+#include <sys/fcntl.h>
+#include <sys/kmem.h>
+#include <sys/kauth.h>
+#include <sys/proc.h>
+
+/* v-node */
+#include <sys/namei.h>
+#include <sys/vnode.h>
+/* devsw */
+#include <sys/conf.h>
+
+#include "v7fs_extern.h"
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_inode.h"
+#include "v7fs_superblock.h"
+
+#ifdef V7FS_VFSOPS_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(arg...)         ((void)0)
+#endif
+
+struct pool v7fs_node_pool;
+
+static int v7fs_mountfs(struct vnode *, struct mount *, int);
+static int v7fs_openfs(struct vnode *, struct mount *, struct lwp *);
+static void v7fs_closefs(struct vnode *, struct mount *);
+static int is_v7fs_partition(struct vnode *);
+static enum vtype v7fs_mode_to_vtype(v7fs_mode_t mode);
+int v7fs_vnode_reload(struct mount *, struct vnode *);
+
+int
+v7fs_mount(struct mount *mp, const char *path, void *data, size_t *data_len)
+{
+       struct lwp *l = curlwp;
+       struct v7fs_args *args = data;
+       struct v7fs_mount *v7fsmount = (void *)mp->mnt_data;
+       struct vnode *devvp = NULL;
+       int error = 0;
+       bool update = mp->mnt_flag & MNT_UPDATE;
+
+       DPRINTF("mnt_flag=%x %s\n", mp->mnt_flag, update ? "update" : "");
+
+       if (*data_len < sizeof(*args))
+               return EINVAL;
+
+       if (mp->mnt_flag & MNT_GETARGS) {
+               if (!v7fsmount)
+                       return EIO;
+               args->fspec = NULL;
+               args->endian = v7fsmount->core->endian;
+               *data_len = sizeof(*args);
+               return 0;
+       }
+
+       DPRINTF("args->fspec=%s endian=%d\n", args->fspec, args->endian);
+       if (args->fspec == NULL) {
+               /* nothing to do. */
+               return EINVAL;
+       }
+
+       if (args->fspec != NULL) {
+               /* Look up the name and verify that it's sane. */
+               error = namei_simple_user(args->fspec,
+                   NSM_FOLLOW_NOEMULROOT, &devvp);
+               if (error != 0)
+                       return (error);
+               DPRINTF("mount device=%lx\n", (long)devvp->v_rdev);
+
+               if (!update) {
+                       /*
+                        * Be sure this is a valid block device
+                        */
+                       if (devvp->v_type != VBLK)
+                               error = ENOTBLK;
+                       else if (bdevsw_lookup(devvp->v_rdev) == NULL)
+                               error = ENXIO;
+               } else {
+                       KDASSERT(v7fsmount);
+                       /*
+                        * Be sure we're still naming the same device
+                        * used for our initial mount
+                        */
+                       if (devvp != v7fsmount->devvp) {
+                               DPRINTF("devvp %p != %p rootvp=%p\n", devvp,
+                                   v7fsmount->devvp, rootvp);
+                               if (rootvp == v7fsmount->devvp) {
+                                       vrele(devvp);
+                                       devvp = rootvp;
+                                       vref(devvp);
+                               } else {
+                                       error = EINVAL;
+                               }
+                       }
+               }
+       }
+
+       /*
+        * If mount by non-root, then verify that user has necessary
+        * permissions on the device.
+        *
+        * Permission to update a mount is checked higher, so here we presume
+        * updating the mount is okay (for example, as far as securelevel goes)
+        * which leaves us with the normal check.
+        */
+       if (error == 0) {
+               int accessmode = VREAD;
+               if (update ?
+                   (mp->mnt_iflag & IMNT_WANTRDWR) != 0 :
+                   (mp->mnt_flag & MNT_RDONLY) == 0)
+                       accessmode |= VWRITE;
+               error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
+                   KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp,
+                   KAUTH_ARG(accessmode));
+       }
+
+       if (error) {
+               vrele(devvp);
+               return error;
+       }
+
+       if (!update) {
+               if ((error = v7fs_openfs(devvp, mp, l))) {
+                       vrele(devvp);
+                       return error;
+               }
+
+               if ((error = v7fs_mountfs(devvp, mp, args->endian))) {
+                       v7fs_closefs(devvp, mp);
+                       VOP_UNLOCK(devvp);
+                       vrele(devvp);
+                       return error;
+               }
+               VOP_UNLOCK(devvp);
+       } else  if (mp->mnt_flag & MNT_RDONLY) {
+               /* XXX: r/w -> read only */
+       }
+
+       return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE,
+           mp->mnt_op->vfs_name, mp, l);
+}
+
+static int
+is_v7fs_partition(struct vnode *devvp)
+{
+       struct dkwedge_info dkw;
+       int error;
+
+       if ((error = getdiskinfo(devvp, &dkw)) != 0) {
+               DPRINTF("getdiskinfo=%d\n", error);
+               return error;
+       }
+       DPRINTF("ptype=%s size=%" PRIu64 "\n", dkw.dkw_ptype, dkw->dkw_size);
+
+       return strcmp(dkw.dkw_ptype, DKW_PTYPE_V7) == 0 ? 0 : EINVAL;
+}
+
+static int
+v7fs_openfs(struct vnode *devvp, struct mount *mp, struct lwp *l)
+{
+       kauth_cred_t cred = l->l_cred;
+       int oflags;
+       int error;
+
+       /* Flush buffer */
+       vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
+       if ((error = vinvalbuf(devvp, V_SAVE, cred, l, 0, 0)))
+               goto unlock_exit;
+
+       /* Open block device */
+       oflags = FREAD;
+       if ((mp->mnt_flag & MNT_RDONLY) == 0)
+               oflags |= FWRITE;
+
+       if ((error = VOP_OPEN(devvp, oflags, NOCRED)) != 0) {
+               DPRINTF("VOP_OPEN=%d\n", error);
+               goto unlock_exit;
+       }
+
+       return 0; /* lock held */
+
+unlock_exit:
+       VOP_UNLOCK(devvp);
+
+       return error;
+}
+
+static void
+v7fs_closefs(struct vnode *devvp, struct mount *mp)
+{
+       int oflags = FREAD;
+
+       if ((mp->mnt_flag & MNT_RDONLY) == 0)
+               oflags |= FWRITE;
+
+       VOP_CLOSE(devvp, oflags, NOCRED);
+}
+
+static int
+v7fs_mountfs(struct vnode *devvp, struct mount *mp, int endian)
+{
+       struct v7fs_mount *v7fsmount;
+       int error;
+       struct v7fs_mount_device mount;
+
+       DPRINTF("%d\n",endian);
+
+       v7fsmount = kmem_zalloc(sizeof(*v7fsmount), KM_SLEEP);
+       if (v7fsmount == NULL) {
+               return ENOMEM;
+       }
+       v7fsmount->devvp = devvp;
+       v7fsmount->mountp = mp;
+
+       mount.device.vnode = devvp;
+       mount.endian = endian;
+
+       if ((error = v7fs_io_init(&v7fsmount->core, &mount, V7FS_BSIZE))) {
+               goto err_exit;
+       }
+       struct v7fs_self *fs = v7fsmount->core;
+
+       if ((error = v7fs_superblock_load(fs))) {
+               v7fs_io_fini(fs);
+               goto err_exit;
+       }
+
+       LIST_INIT(&v7fsmount->v7fs_node_head);
+
+       mp->mnt_data = v7fsmount;
+       mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)devvp->v_rdev;
+       mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_V7FS);
+       mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
+       mp->mnt_stat.f_namemax = V7FS_NAME_MAX;
+       mp->mnt_flag |= MNT_LOCAL;
+       mp->mnt_dev_bshift = V7FS_BSHIFT;
+       mp->mnt_fs_bshift = V7FS_BSHIFT;
+
+       return 0;
+
+err_exit:
+       kmem_free(v7fsmount, sizeof(*v7fsmount));
+       return error;
+}
+
+int
+v7fs_start(struct mount *mp, int flags)
+{
+
+       DPRINTF("\n");
+       /* Nothing to do. */
+       return 0;
+}
+
+int
+v7fs_unmount(struct mount *mp, int mntflags)
+{
+       struct v7fs_mount *v7fsmount = (void *)mp->mnt_data;
+       int error;
+
+       DPRINTF("%p\n", v7fsmount);
+
+       if ((error = vflush(mp, NULLVP,
+                   mntflags & MNT_FORCE ? FORCECLOSE : 0)) != 0)
+               return error;
+
+       vn_lock(v7fsmount->devvp, LK_EXCLUSIVE | LK_RETRY);
+       error = VOP_CLOSE(v7fsmount->devvp, FREAD, NOCRED);
+       vput(v7fsmount->devvp);
+
+       v7fs_io_fini(v7fsmount->core);
+
+       kmem_free(v7fsmount, sizeof(*v7fsmount));
+       mp->mnt_data = NULL;
+       mp->mnt_flag &= ~MNT_LOCAL;
+
+       return 0;
+}
+
+int
+v7fs_root(struct mount *mp, struct vnode **vpp)
+{
+       struct vnode *vp;
+       int error;
+
+       DPRINTF("\n");
+       if ((error = VFS_VGET(mp, V7FS_ROOT_INODE, &vp)) != 0) {
+               DPRINTF("error=%d\n", error);
+               return error;
+       }
+       *vpp = vp;
+       DPRINTF("done.\n");
+
+       return 0;
+}
+
+int
+v7fs_statvfs(struct mount *mp, struct statvfs *f)
+{
+       struct v7fs_mount *v7fsmount = mp->mnt_data;
+       struct v7fs_self *fs = v7fsmount->core;
+
+       DPRINTF("scratch remain=%d\n", fs->scratch_remain);
+
+       v7fs_superblock_status(fs);
+
+       f->f_bsize = V7FS_BSIZE;
+       f->f_frsize = V7FS_BSIZE;
+       f->f_iosize = V7FS_BSIZE;
+       f->f_blocks = fs->stat.total_blocks;
+       f->f_bfree = fs->stat.free_blocks;
+       f->f_bavail = fs->stat.free_blocks;
+       f->f_bresvd = 0;
+       f->f_files = fs->stat.total_files;
+       f->f_ffree = fs->stat.free_inode;
+       f->f_favail = f->f_ffree;
+       f->f_fresvd = 0;
+       copy_statvfs_info(f, mp);
+
+       return 0;
+}
+
+int
+v7fs_sync(struct mount *mp, int waitfor, kauth_cred_t cred)
+{
+       struct v7fs_mount *v7fsmount = mp->mnt_data;
+       struct v7fs_self *fs = v7fsmount->core;
+       struct v7fs_node *v7fs_node;
+       struct v7fs_inode *inode;
+       struct vnode *v;
+       int err, error;
+       int retry_cnt;
+
+       DPRINTF("\n");
+
+       v7fs_superblock_writeback(fs);
+       for (retry_cnt = 0; retry_cnt < 2; retry_cnt++) {
+               error = 0;
+
+               mutex_enter(&mntvnode_lock);
+               for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head);
+                   v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) {
+                       inode = &v7fs_node->inode;
+                       if (!v7fs_inode_allocated(inode)) {
+                               continue;
+                       }
+                       v = v7fs_node->vnode;
+                       mutex_enter(v->v_interlock);
+                       mutex_exit(&mntvnode_lock);
+                       err = vget(v, LK_EXCLUSIVE | LK_NOWAIT);
+                       if (err == 0) {
+                               err = VOP_FSYNC(v, cred, FSYNC_WAIT, 0, 0);
+                               vput(v);
+                       }
+                       if (err != 0)
+                               error = err;
+                       mutex_enter(&mntvnode_lock);
+               }
+               mutex_exit(&mntvnode_lock);
+
+               if (error == 0)
+                       break;
+       }
+
+       return error;
+}
+
+static enum vtype
+v7fs_mode_to_vtype (v7fs_mode_t mode)
+{
+       enum vtype table[] = { VCHR, VDIR, VBLK, VREG, VLNK, VSOCK };
+
+       if ((mode & V7FS_IFMT) == V7FSBSD_IFFIFO)
+               return VFIFO;
+
+       return table[((mode >> 13) & 7) - 1];
+}
+
+int
+v7fs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
+{
+       struct v7fs_mount *v7fsmount = mp->mnt_data;
+       struct v7fs_self *fs = v7fsmount->core;
+       struct vnode *vp;
+       struct v7fs_node *v7fs_node;
+       struct v7fs_inode inode;
+       int error;
+
+       /* Lookup requested i-node */
+       if ((error = v7fs_inode_load(fs, &inode, ino))) {
+               DPRINTF("v7fs_inode_load failed.\n");
+               return error;
+       }
+
+retry:
+       mutex_enter(&mntvnode_lock);
+       for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head);
+           v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) {
+               if (v7fs_node->inode.inode_number == ino) {
+                       vp = v7fs_node->vnode;
+                       mutex_enter(vp->v_interlock);
+                       mutex_exit(&mntvnode_lock);
+                       if (vget(vp, LK_EXCLUSIVE) == 0) {
+                               *vpp = vp;
+                               return 0;
+                       } else {
+                               DPRINTF("retry!\n");
+                               goto retry;
+                       }
+               }
+       }
+       mutex_exit(&mntvnode_lock);
+
+       /* Allocate v-node. */
+       if ((error = getnewvnode(VT_V7FS, mp, v7fs_vnodeop_p, NULL, &vp))) {
+               DPRINTF("getnewvnode error.\n");
+               return error;
+       }
+       /* Lock vnode here */
+       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+       /* Allocate i-node */
+       vp->v_data = pool_get(&v7fs_node_pool, PR_WAITOK);
+       memset(vp->v_data, 0, sizeof(*v7fs_node));
+       v7fs_node = vp->v_data;
+       mutex_enter(&mntvnode_lock);
+       LIST_INSERT_HEAD(&v7fsmount->v7fs_node_head, v7fs_node, link);
+       mutex_exit(&mntvnode_lock);
+       v7fs_node->vnode = vp;
+       v7fs_node->v7fsmount = v7fsmount;
+       v7fs_node->inode = inode;/*structure copy */
+       v7fs_node->lockf = NULL; /* advlock */
+
+       genfs_node_init(vp, &v7fs_genfsops);
+       uvm_vnp_setsize(vp, v7fs_inode_filesize(&inode));
+
+       if (ino == V7FS_ROOT_INODE) {
+               vp->v_type = VDIR;
+               vp->v_vflag |= VV_ROOT;
+       } else {
+               vp->v_type = v7fs_mode_to_vtype(inode.mode);
+
+               if (vp->v_type == VBLK || vp->v_type == VCHR) {
+                       dev_t rdev = inode.device;
+                       vp->v_op = v7fs_specop_p;
+                       spec_node_init(vp, rdev);
+               } else if (vp->v_type == VFIFO) {
+                       vp->v_op = v7fs_fifoop_p;
+               }
+       }
+
+       *vpp = vp;
+
+       return 0;
+}
+
+
+int
+v7fs_fhtovp(struct mount *mp, struct fid *fid, struct vnode **vpp)
+{
+
+       DPRINTF("\n");
+       /* notyet */
+       return EOPNOTSUPP;
+}
+
+int
+v7fs_vptofh(struct vnode *vpp, struct fid *fid, size_t *fh_size)
+{
+
+       DPRINTF("\n");
+       /* notyet */
+       return EOPNOTSUPP;
+}
+
+void
+v7fs_init(void)
+{
+
+       DPRINTF("\n");
+       pool_init(&v7fs_node_pool, sizeof(struct v7fs_node), 0, 0, 0,
+           "v7fs_node_pool", &pool_allocator_nointr, IPL_NONE);
+}
+
+void
+v7fs_reinit(void)
+{
+
+       /* Nothing to do. */
+       DPRINTF("\n");
+}
+
+void
+v7fs_done(void)
+{
+
+       DPRINTF("\n");
+       pool_destroy(&v7fs_node_pool);
+}
+
+int
+v7fs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags,
+    kauth_cred_t cred)
+{
+
+       DPRINTF("\n");
+       return 0;
+}
+
+int
+v7fs_mountroot(void)
+{
+       struct mount *mp;
+       int error;
+
+       DPRINTF("");
+       /* On mountroot, devvp (rootdev) is opened by vfs_mountroot */
+       if ((error = is_v7fs_partition (rootvp)))
+               return error;
+
+       if ((error = vfs_rootmountalloc(MOUNT_V7FS, "root_device", &mp))) {
+               DPRINTF("mountalloc error=%d\n", error);
+               vrele(rootvp);
+               return error;
+       }
+
+       if ((error = v7fs_mountfs(rootvp, mp, _BYTE_ORDER))) {
+               DPRINTF("mountfs error=%d\n", error);
+               vfs_unbusy(mp, false, NULL);
+               vfs_destroy(mp);
+               return error;
+       }
+
+       mountlist_append(mp);
+
+       vfs_unbusy(mp, false, NULL);
+
+       return 0;
+}
+
+/* Reload disk inode information */
+int
+v7fs_vnode_reload(struct mount *mp, struct vnode *vp)
+{
+       struct v7fs_mount *v7fsmount = mp->mnt_data;
+       struct v7fs_self *fs = v7fsmount->core;
+       struct v7fs_node *v7fs_node;
+       struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode;
+       int target_ino = inode->inode_number;
+       int error = 0;
+
+       DPRINTF("#%d\n", target_ino);
+       mutex_enter(&mntvnode_lock);
+       for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head);
+            v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) {
+               inode = &v7fs_node->inode;
+               if (!v7fs_inode_allocated(inode)) {
+                       continue;
+               }
+               if (inode->inode_number == target_ino) {
+                       error = v7fs_inode_load(fs, &v7fs_node->inode,
+                           target_ino);
+                       DPRINTF("sync #%d error=%d\n", target_ino, error);
+                       break;
+               }
+       }
+       mutex_exit(&mntvnode_lock);
+
+       return error;
+}
diff --git a/sys/fs/v7fs/v7fs_vnops.c b/sys/fs/v7fs/v7fs_vnops.c
new file mode 100644 (file)
index 0000000..3491f81
--- /dev/null
@@ -0,0 +1,1328 @@
+/*     $NetBSD: v7fs_vnops.c,v 1.13 2013/11/20 23:44:23 rmind Exp $    */
+
+/*-
+ * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_vnops.c,v 1.13 2013/11/20 23:44:23 rmind Exp $");
+#if defined _KERNEL_OPT
+#include "opt_v7fs.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/resource.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/dirent.h>
+#include <sys/kmem.h>
+#include <sys/lockf.h>
+#include <sys/unistd.h>
+#include <sys/fcntl.h>
+#include <sys/kauth.h>
+#include <sys/buf.h>
+#include <sys/stat.h>  /*APPEND */
+#include <miscfs/genfs/genfs.h>
+
+#include <fs/v7fs/v7fs.h>
+#include <fs/v7fs/v7fs_impl.h>
+#include <fs/v7fs/v7fs_inode.h>
+#include <fs/v7fs/v7fs_dirent.h>
+#include <fs/v7fs/v7fs_file.h>
+#include <fs/v7fs/v7fs_datablock.h>
+#include <fs/v7fs/v7fs_extern.h>
+
+#ifdef V7FS_VNOPS_DEBUG
+#define        DPRINTF(fmt, args...)   printf("%s: " fmt, __func__, ##args)
+#else
+#define        DPRINTF(arg...)         ((void)0)
+#endif
+
+int v7fs_vnode_reload(struct mount *, struct vnode *);
+
+static v7fs_mode_t vtype_to_v7fs_mode(enum vtype);
+static uint8_t v7fs_mode_to_d_type(v7fs_mode_t);
+
+static v7fs_mode_t
+vtype_to_v7fs_mode(enum vtype type)
+{
+       /* Convert Vnode types to V7FS types (sys/vnode.h)*/
+       v7fs_mode_t table[] = { 0, V7FS_IFREG, V7FS_IFDIR, V7FS_IFBLK,
+                               V7FS_IFCHR, V7FSBSD_IFLNK, V7FSBSD_IFSOCK,
+                               V7FSBSD_IFFIFO };
+       return table[type];
+}
+
+static uint8_t
+v7fs_mode_to_d_type(v7fs_mode_t mode)
+{
+       /* Convert V7FS types to dirent d_type (sys/dirent.h)*/
+
+       return (mode & V7FS_IFMT) >> 12;
+}
+
+int
+v7fs_lookup(void *v)
+{
+       struct vop_lookup_args /* {
+                                 struct vnode *a_dvp;
+                                 struct vnode **a_vpp;
+                                 struct componentname *a_cnp;
+                                 } */ *a = v;
+       struct vnode *dvp = a->a_dvp;
+       struct v7fs_node *parent_node = dvp->v_data;
+       struct v7fs_inode *parent = &parent_node->inode;
+       struct v7fs_self *fs = parent_node->v7fsmount->core;/* my filesystem */
+       struct vnode *vpp;
+       struct componentname *cnp = a->a_cnp;
+       int nameiop = cnp->cn_nameiop;
+       const char *name = cnp->cn_nameptr;
+       int namelen = cnp->cn_namelen;
+       int flags = cnp->cn_flags;
+       bool isdotdot = flags & ISDOTDOT;
+       bool islastcn = flags & ISLASTCN;
+       v7fs_ino_t ino;
+       int error;
+#ifdef V7FS_VNOPS_DEBUG
+       const char *opname[] = { "LOOKUP", "CREATE", "DELETE", "RENAME" };
+#endif
+       DPRINTF("'%s' op=%s flags=%d parent=%d %o %dbyte\n", name,
+           opname[nameiop], cnp->cn_flags, parent->inode_number, parent->mode,
+           parent->filesize);
+
+       *a->a_vpp = 0;
+
+       /* Check directory permission for search */
+       if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred))) {
+               DPRINTF("***perm.\n");
+               return error;
+       }
+
+       /* Deny last component write operation on a read-only mount */
+       if (islastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
+           (nameiop == DELETE || nameiop == RENAME)) {
+               DPRINTF("***ROFS.\n");
+               return EROFS;
+       }
+
+       /* "." */
+       if (namelen == 1 && name[0] == '.') {
+               if ((nameiop == RENAME) && islastcn) {
+                       return EISDIR; /* t_vnops rename_dir(3) */
+               }
+               vref(dvp); /* v_usecount++ */
+               *a->a_vpp = dvp;
+               DPRINTF("done.(.)\n");
+               return 0;
+       }
+
+       /* ".." and reguler file. */
+       if ((error = v7fs_file_lookup_by_name(fs, parent, name, &ino))) {
+               /* Not found. Tell this entry be able to allocate. */
+               if (((nameiop == CREATE) || (nameiop == RENAME)) && islastcn) {
+                       /* Check directory permission to allocate. */
+                       if ((error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred))) {
+                               DPRINTF("access denied. (%s)\n", name);
+                               return error;
+                       }
+                       DPRINTF("EJUSTRETURN op=%d (%s)\n", nameiop, name);
+                       return EJUSTRETURN;
+               }
+               DPRINTF("lastcn=%d\n", flags & ISLASTCN);
+               return error;
+       }
+
+       if ((nameiop == DELETE) && islastcn) {
+               if ((error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred))) {
+                       DPRINTF("access denied. (%s)\n", name);
+                       return error;
+               }
+       }
+
+       /* Entry found. Allocate v-node */
+       // Check permissions?
+       vpp = 0;
+       if (isdotdot) {
+               VOP_UNLOCK(dvp); /* preserve reference count. (not vput) */
+       }
+       DPRINTF("enter vget\n");
+       if ((error = v7fs_vget(dvp->v_mount, ino, &vpp))) {
+               DPRINTF("***can't get vnode.\n");
+               return error;
+       }
+       DPRINTF("exit vget\n");
+       if (isdotdot) {
+               vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+       }
+       *a->a_vpp = vpp;
+       DPRINTF("done.(%s)\n", name);
+
+       return 0;
+}
+
+int
+v7fs_create(void *v)
+{
+       struct vop_create_args /* {
+                                 struct vnode *a_dvp;
+                                 struct vnode **a_vpp;
+                                 struct componentname *a_cnp;
+                                 struct vattr *a_vap;
+                                 } */ *a = v;
+       struct v7fs_node *parent_node = a->a_dvp->v_data;
+       struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
+       struct v7fs_self *fs = v7fsmount->core;
+       struct mount *mp = v7fsmount->mountp;
+       struct v7fs_fileattr attr;
+       struct vattr *va = a->a_vap;
+       kauth_cred_t cr = a->a_cnp->cn_cred;
+       v7fs_ino_t ino;
+       int error = 0;
+
+       DPRINTF("%s parent#%d\n", a->a_cnp->cn_nameptr,
+           parent_node->inode.inode_number);
+       KDASSERT((va->va_type == VREG) || (va->va_type == VSOCK));
+
+       memset(&attr, 0, sizeof(attr));
+       attr.uid = kauth_cred_geteuid(cr);
+       attr.gid = kauth_cred_getegid(cr);
+       attr.mode = va->va_mode | vtype_to_v7fs_mode (va->va_type);
+       attr.device = 0;
+
+       /* Allocate disk entry. and register its entry to parent directory. */
+       if ((error = v7fs_file_allocate(fs, &parent_node->inode,
+                   a->a_cnp->cn_nameptr, &attr, &ino))) {
+               DPRINTF("v7fs_file_allocate failed.\n");
+               goto unlock_exit;
+       }
+       /* Sync dirent size change. */
+       uvm_vnp_setsize(a->a_dvp, v7fs_inode_filesize(&parent_node->inode));
+
+       /* Get myself vnode. */
+       *a->a_vpp = 0;
+       if ((error = v7fs_vget(mp, ino, a->a_vpp))) {
+               DPRINTF("v7fs_vget failed.\n");
+               goto unlock_exit;
+       }
+
+       /* Scheduling update time. real update by v7fs_update */
+       struct v7fs_node *newnode = (*a->a_vpp)->v_data;
+       newnode->update_ctime = true;
+       newnode->update_mtime = true;
+       newnode->update_atime = true;
+       DPRINTF("allocated %s->#%d\n", a->a_cnp->cn_nameptr, ino);
+
+unlock_exit:
+       /* unlock parent directory */
+       vput(a->a_dvp); /* locked at v7fs_lookup(); */
+
+       return error;
+}
+
+int
+v7fs_mknod(void *v)
+{
+       struct vop_mknod_args /* {
+                                struct vnode           *a_dvp;
+                                struct vnode           **a_vpp;
+                                struct componentname   *a_cnp;
+                                struct vattr           *a_vap;
+                                } */ *a = v;
+       struct componentname *cnp = a->a_cnp;
+       kauth_cred_t cr = cnp->cn_cred;
+       struct vnode *dvp = a->a_dvp;
+       struct vattr *va = a->a_vap;
+       struct v7fs_node *parent_node = dvp->v_data;
+       struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
+       struct v7fs_self *fs = v7fsmount->core;
+       struct mount *mp = v7fsmount->mountp;
+       struct v7fs_fileattr attr;
+
+       v7fs_ino_t ino;
+       int error = 0;
+
+       DPRINTF("%s %06o %lx %d\n", cnp->cn_nameptr, va->va_mode,
+           (long)va->va_rdev, va->va_type);
+       memset(&attr, 0, sizeof(attr));
+       attr.uid = kauth_cred_geteuid(cr);
+       attr.gid = kauth_cred_getegid(cr);
+       attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type);
+       attr.device = va->va_rdev;
+
+       if ((error = v7fs_file_allocate(fs, &parent_node->inode,
+           cnp->cn_nameptr, &attr, &ino)))
+               goto unlock_exit;
+       /* Sync dirent size change. */
+       uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode));
+
+       if ((error = v7fs_vget(mp, ino, a->a_vpp))) {
+               DPRINTF("can't get vnode.\n");
+               goto unlock_exit;
+       }
+       struct v7fs_node *newnode = (*a->a_vpp)->v_data;
+       newnode->update_ctime = true;
+       newnode->update_mtime = true;
+       newnode->update_atime = true;
+
+unlock_exit:
+       vput(dvp);
+
+       return error;
+}
+
+int
+v7fs_open(void *v)
+{
+       struct vop_open_args /* {
+                               struct vnode *a_vp;
+                               int  a_mode;
+                               kauth_cred_t a_cred;
+                               } */ *a = v;
+
+       struct vnode *vp = a->a_vp;
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_inode *inode = &v7node->inode;
+
+       DPRINTF("inode %d\n", inode->inode_number);
+       /* Append mode file pointer is managed by kernel. */
+       if (inode->append_mode &&
+           ((a->a_mode & (FWRITE | O_APPEND)) == FWRITE)) {
+               DPRINTF("file is already opened by append mode.\n");
+               return EPERM;
+       }
+
+       return 0;
+}
+
+int
+v7fs_close(void *v)
+{
+       struct vop_close_args /* {
+                                struct vnodeop_desc *a_desc;
+                                struct vnode *a_vp;
+                                int  a_fflag;
+                                kauth_cred_t a_cred;
+                                } */ *a = v;
+       struct vnode *vp = a->a_vp;
+#ifdef V7FS_VNOPS_DEBUG
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_inode *inode = &v7node->inode;
+#endif
+       DPRINTF("#%d (i)%dbyte (v)%zubyte\n", inode->inode_number,
+           v7fs_inode_filesize(inode), vp->v_size);
+
+       /* Update timestamp */
+       v7fs_update(vp, 0, 0, UPDATE_WAIT);
+
+       return 0;
+}
+
+static int
+v7fs_check_possible(struct vnode *vp, struct v7fs_node *v7node,
+    mode_t mode)
+{
+
+       if (!(mode & VWRITE))
+         return 0;
+
+       switch (vp->v_type) {
+       default:
+               /*  special file is always writable. */
+               return 0;
+       case VDIR:
+       case VLNK:
+       case VREG:
+               break;
+       }
+
+       return vp->v_mount->mnt_flag & MNT_RDONLY ? EROFS : 0;
+}
+
+static int
+v7fs_check_permitted(struct vnode *vp, struct v7fs_node *v7node,
+    mode_t mode, kauth_cred_t cred)
+{
+
+       struct v7fs_inode *inode = &v7node->inode;
+
+       return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode,
+           vp->v_type, inode->mode), vp, NULL, genfs_can_access(vp->v_type,
+           inode->mode, inode->uid, inode->gid, mode, cred));
+}
+
+int
+v7fs_access(void *v)
+{
+       struct vop_access_args /* {
+                                 struct vnode  *a_vp;
+                                 int           a_mode;
+                                 kauth_cred_t  a_cred;
+                                 } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct v7fs_node *v7node = vp->v_data;
+       int error;
+
+       error = v7fs_check_possible(vp, v7node, ap->a_mode);
+       if (error)
+               return error;
+
+       error = v7fs_check_permitted(vp, v7node, ap->a_mode, ap->a_cred);
+
+       return error;
+}
+
+int
+v7fs_getattr(void *v)
+{
+       struct vop_getattr_args /* {
+                                  struct vnode *a_vp;
+                                  struct vattr *a_vap;
+                                  kauth_cred_t a_cred;
+                                  } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_inode *inode = &v7node->inode;
+       struct v7fs_mount *v7fsmount = v7node->v7fsmount;
+       struct vattr *vap = ap->a_vap;
+
+       DPRINTF("\n");
+       vap->va_type = vp->v_type;
+       vap->va_mode = inode->mode;
+       vap->va_nlink = inode->nlink;
+       vap->va_uid = inode->uid;
+       vap->va_gid = inode->gid;
+       vap->va_fsid = v7fsmount->devvp->v_rdev;
+       vap->va_fileid = inode->inode_number;
+       vap->va_size = vp->v_size;
+       vap->va_atime.tv_sec = inode->atime;
+       vap->va_mtime.tv_sec = inode->mtime;
+       vap->va_ctime.tv_sec = inode->ctime;
+       vap->va_birthtime.tv_sec = 0;
+       vap->va_gen = 1;
+       vap->va_flags = inode->append_mode ? SF_APPEND : 0;
+       vap->va_rdev = inode->device;
+       vap->va_bytes = vap->va_size; /* No sparse support. */
+       vap->va_filerev = 0;
+       vap->va_vaflags = 0;
+       /* PAGE_SIZE is larger than sizeof(struct dirent). OK.
+          getcwd_scandir()@vfs_getcwd.c */
+       vap->va_blocksize = PAGE_SIZE;
+
+       return 0;
+}
+
+int
+v7fs_setattr(void *v)
+{
+       struct vop_setattr_args /* {
+                                  struct vnode *a_vp;
+                                  struct vattr *a_vap;
+                                  kauth_cred_t a_cred;
+                                  struct proc *p;
+                                  } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+       struct vattr *vap = ap->a_vap;
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_self *fs = v7node->v7fsmount->core;
+       struct v7fs_inode *inode = &v7node->inode;
+       kauth_cred_t cred = ap->a_cred;
+       struct timespec *acc, *mod;
+       int error = 0;
+       acc = mod = NULL;
+
+       DPRINTF("\n");
+
+       if (vp->v_mount->mnt_flag & MNT_RDONLY) {
+               switch (vp->v_type) {
+               default:
+                       /*  special file is always writable. */
+                       break;
+               case VDIR:
+               case VLNK:
+               case VREG:
+                       DPRINTF("read-only mount\n");
+                       return EROFS;
+               }
+       }
+
+       if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
+           (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
+           (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
+           ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
+               DPRINTF("invalid request\n");
+               return EINVAL;
+       }
+       /* File pointer mode. */
+       if (vap->va_flags != VNOVAL) {
+               error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_FLAGS,
+                   vp, NULL, genfs_can_chflags(cred, vp->v_type, inode->uid,
+                   false));
+               if (error)
+                       return error;
+               inode->append_mode = vap->va_flags & SF_APPEND;
+       }
+
+       /* File size change. */
+       if ((vap->va_size != VNOVAL) && (vp->v_type == VREG)) {
+               error = v7fs_datablock_size_change(fs, vap->va_size, inode);
+               if (error == 0)
+                       uvm_vnp_setsize(vp, vap->va_size);
+       }
+       uid_t uid = inode->uid;
+       gid_t gid = inode->gid;
+
+       if (vap->va_uid != (uid_t)VNOVAL) {
+               uid = vap->va_uid;
+               error = kauth_authorize_vnode(cred,
+                   KAUTH_VNODE_CHANGE_OWNERSHIP, vp, NULL,
+                   genfs_can_chown(cred, inode->uid, inode->gid, uid,
+                   gid));
+               if (error)
+                       return error;
+               inode->uid = uid;
+       }
+       if (vap->va_gid != (uid_t)VNOVAL) {
+               gid = vap->va_gid;
+               error = kauth_authorize_vnode(cred,
+                   KAUTH_VNODE_CHANGE_OWNERSHIP, vp, NULL,
+                   genfs_can_chown(cred, inode->uid, inode->gid, uid,
+                   gid));
+               if (error)
+                       return error;
+               inode->gid = gid;
+       }
+       if (vap->va_mode != (mode_t)VNOVAL) {
+               mode_t mode = vap->va_mode;
+               error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY,
+                   vp, NULL, genfs_can_chmod(vp->v_type, cred, inode->uid, inode->gid,
+                   mode));
+               if (error) {
+                       return error;
+               }
+               v7fs_inode_chmod(inode, mode);
+       }
+       if ((vap->va_atime.tv_sec != VNOVAL) ||
+           (vap->va_mtime.tv_sec != VNOVAL) ||
+           (vap->va_ctime.tv_sec != VNOVAL)) {
+               error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp,
+                   NULL, genfs_can_chtimes(vp, vap->va_vaflags, inode->uid,
+                   cred));
+               if (error)
+                       return error;
+
+               if (vap->va_atime.tv_sec != VNOVAL) {
+                       acc = &vap->va_atime;
+               }
+               if (vap->va_mtime.tv_sec != VNOVAL) {
+                       mod = &vap->va_mtime;
+                       v7node->update_mtime = true;
+               }
+               if (vap->va_ctime.tv_sec != VNOVAL) {
+                       v7node->update_ctime = true;
+               }
+       }
+
+       v7node->update_atime = true;
+       v7fs_update(vp, acc, mod, 0);
+
+       return error;
+}
+
+int
+v7fs_read(void *v)
+{
+       struct vop_read_args /* {
+                               struct vnode *a_vp;
+                               struct uio *a_uio;
+                               int a_ioflag;
+                               kauth_cred_t a_cred;
+                               } */ *a = v;
+       struct vnode *vp = a->a_vp;
+       struct uio *uio = a->a_uio;
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_inode *inode = &v7node->inode;
+       vsize_t sz, filesz = v7fs_inode_filesize(inode);
+       const int advice = IO_ADV_DECODE(a->a_ioflag);
+       int error = 0;
+
+       DPRINTF("type=%d inode=%d\n", vp->v_type, v7node->inode.inode_number);
+
+       while (uio->uio_resid > 0) {
+               if ((sz = MIN(filesz - uio->uio_offset, uio->uio_resid)) == 0)
+                       break;
+
+               error = ubc_uiomove(&vp->v_uobj, uio, sz, advice, UBC_READ |
+                   UBC_PARTIALOK | UBC_UNMAP_FLAG(v));
+               if (error) {
+                       break;
+               }
+               DPRINTF("read %zubyte\n", sz);
+       }
+       v7node->update_atime = true;
+
+       return  error;
+}
+
+int
+v7fs_write(void *v)
+{
+       struct vop_write_args /* {
+                                struct vnode *a_vp;
+                                struct uio *a_uio;
+                                int  a_ioflag;
+                                kauth_cred_t a_cred;
+                                } */ *a = v;
+       struct vnode *vp = a->a_vp;
+       struct uio *uio = a->a_uio;
+       int advice = IO_ADV_DECODE(a->a_ioflag);
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_inode *inode = &v7node->inode;
+       struct v7fs_self *fs = v7node->v7fsmount->core;
+       vsize_t sz;
+       int error = 0;
+
+       if (uio->uio_resid == 0)
+               return 0;
+
+       sz = v7fs_inode_filesize(inode);
+       DPRINTF("(i)%ld (v)%zu ofs=%zu + res=%zu = %zu\n", sz, vp->v_size,
+           uio->uio_offset, uio->uio_resid, uio->uio_offset + uio->uio_resid);
+
+       /* Append mode file offset is managed by kernel. */
+       if (a->a_ioflag & IO_APPEND)
+               uio->uio_offset = sz;
+
+       /* If write region is over filesize, expand. */
+       size_t newsize= uio->uio_offset + uio->uio_resid;
+       ssize_t expand = newsize - sz;
+       if (expand > 0) {
+               if ((error = v7fs_datablock_expand(fs, inode, expand)))
+                       return error;
+               uvm_vnp_setsize(vp, newsize);
+       }
+
+       while (uio->uio_resid > 0) {
+               sz = uio->uio_resid;
+               if ((error = ubc_uiomove(&vp->v_uobj, uio, sz, advice,
+                           UBC_WRITE | UBC_UNMAP_FLAG(v))))
+                       break;
+               DPRINTF("write %zubyte\n", sz);
+       }
+       v7node->update_mtime = true;
+
+       return error;
+}
+
+int
+v7fs_fsync(void *v)
+{
+       struct vop_fsync_args /* {
+                                struct vnode *a_vp;
+                                kauth_cred_t a_cred;
+                                int a_flags;
+                                off_t offlo;
+                                off_t offhi;
+                                } */ *a = v;
+       struct vnode *vp = a->a_vp;
+       int error, wait;
+
+       DPRINTF("%p\n", a->a_vp);
+       if (a->a_flags & FSYNC_CACHE) {
+               return EOPNOTSUPP;
+       }
+
+       wait = (a->a_flags & FSYNC_WAIT);
+       error = vflushbuf(vp, a->a_flags);
+
+       if (error == 0 && (a->a_flags & FSYNC_DATAONLY) == 0)
+               error = v7fs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0);
+
+       return error;
+}
+
+int
+v7fs_remove(void *v)
+{
+       struct vop_remove_args /* {
+                                 struct vnodeop_desc *a_desc;
+                                 struct vnode * a_dvp;
+                                 struct vnode * a_vp;
+                                 struct componentname * a_cnp;
+                                 } */ *a = v;
+       struct v7fs_node *parent_node = a->a_dvp->v_data;
+       struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
+       struct vnode *vp = a->a_vp;
+       struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode;
+       struct vnode *dvp = a->a_dvp;
+       struct v7fs_self *fs = v7fsmount->core;
+       bool remove;
+       int error = 0;
+
+       DPRINTF("delete %s\n", a->a_cnp->cn_nameptr);
+
+       if (vp->v_type == VDIR) {
+               error = EPERM;
+               goto out;
+       }
+
+       remove = v7fs_inode_nlink(inode) == 1;
+       if (remove)
+               uvm_vnp_setsize(vp, 0);
+
+       if ((error = v7fs_file_deallocate(fs, &parent_node->inode,
+                   a->a_cnp->cn_nameptr))) {
+               DPRINTF("v7fs_file_delete failed.\n");
+               goto out;
+       }
+       /* Sync dirent size change. */
+       uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode));
+       /* This inode is no longer used. -> v7fs_inactive */
+       if (remove)
+               memset(inode, 0, sizeof(*inode));
+
+out:
+       if (dvp == vp)
+               vrele(vp); /* v_usecount-- of unlocked vp */
+       else
+               vput(vp); /* unlock vp and then v_usecount-- */
+       vput(dvp);
+
+       return error;
+}
+
+int
+v7fs_link(void *v)
+{
+       struct vop_link_args /* {
+                               struct vnode *a_dvp;
+                               struct vnode *a_vp;
+                               struct componentname *a_cnp;
+                               } */ *a = v;
+       struct vnode *dvp = a->a_dvp;
+       struct vnode *vp = a->a_vp;
+       struct v7fs_node *parent_node = dvp->v_data;
+       struct v7fs_node *node = vp->v_data;
+       struct v7fs_inode *parent = &parent_node->inode;
+       struct v7fs_inode *p = &node->inode;
+       struct v7fs_self *fs = node->v7fsmount->core;
+       struct componentname *cnp = a->a_cnp;
+       int error = 0;
+
+       DPRINTF("%p\n", vp);
+       /* Lock soruce file */
+       if ((error = vn_lock(vp, LK_EXCLUSIVE))) {
+               DPRINTF("lock failed. %p\n", vp);
+               VOP_ABORTOP(dvp, cnp);
+               goto unlock;
+       }
+       error = v7fs_file_link(fs, parent, p, cnp->cn_nameptr);
+       /* Sync dirent size change. */
+       uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode));
+
+       VOP_UNLOCK(vp);
+unlock:
+       vput(dvp);
+
+       return error;
+}
+
+int
+v7fs_rename(void *v)
+{
+       struct vop_rename_args /* {
+                                 struct vnode *a_fdvp; from parent-directory
+                                 struct vnode *a_fvp;  from file
+                                 struct componentname *a_fcnp;
+                                 struct vnode *a_tdvp; to parent-directory
+                                 struct vnode *a_tvp;  to file
+                                 struct componentname *a_tcnp;
+                                 } */ *a = v;
+       struct vnode *fvp = a->a_fvp;
+       struct vnode *tvp = a->a_tvp;
+       struct vnode *fdvp = a->a_fdvp;
+       struct vnode *tdvp = a->a_tdvp;
+       struct v7fs_node *parent_from = fdvp->v_data;
+       struct v7fs_node *parent_to = tdvp->v_data;
+       struct v7fs_node *v7node = fvp->v_data;
+       struct v7fs_self *fs = v7node->v7fsmount->core;
+       const char *from_name = a->a_fcnp->cn_nameptr;
+       const char *to_name = a->a_tcnp->cn_nameptr;
+       int error;
+
+       DPRINTF("%s->%s %p %p\n", from_name, to_name, fvp, tvp);
+
+       if ((fvp->v_mount != tdvp->v_mount) ||
+           (tvp && (fvp->v_mount != tvp->v_mount))) {
+               error = EXDEV;
+               DPRINTF("cross-device link\n");
+               goto out;
+       }
+       // XXXsource file lock?
+       error = v7fs_file_rename(fs, &parent_from->inode, from_name,
+           &parent_to->inode, to_name);
+       /* 'to file' inode may be changed. (hard-linked and it is cached.)
+          t_vnops rename_reg_nodir */
+       if (tvp) {
+               v7fs_vnode_reload(parent_from->v7fsmount->mountp, tvp);
+       }
+       /* Sync dirent size change. */
+       uvm_vnp_setsize(tdvp, v7fs_inode_filesize(&parent_to->inode));
+       uvm_vnp_setsize(fdvp, v7fs_inode_filesize(&parent_from->inode));
+out:
+       if (tvp)
+               vput(tvp);  /* locked on entry */
+       if (tdvp == tvp)
+               vrele(tdvp);
+       else
+               vput(tdvp);
+       vrele(fdvp);
+       vrele(fvp);
+
+       return error;
+}
+
+int
+v7fs_mkdir(void *v)
+{
+       struct vop_mkdir_args /* {
+                                struct vnode           *a_dvp;
+                                struct vnode           **a_vpp;
+                                struct componentname   *a_cnp;
+                                struct vattr           *a_vap;
+                                } */ *a = v;
+       struct componentname *cnp = a->a_cnp;
+       kauth_cred_t cr = cnp->cn_cred;
+       struct vnode *dvp = a->a_dvp;
+       struct vattr *va = a->a_vap;
+       struct v7fs_node *parent_node = dvp->v_data;
+       struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
+       struct v7fs_self *fs = v7fsmount->core;
+       struct v7fs_fileattr attr;
+       struct mount *mp = v7fsmount->mountp;
+       v7fs_ino_t ino;
+       int error = 0;
+
+       DPRINTF("\n");
+       memset(&attr, 0, sizeof(attr));
+       attr.uid = kauth_cred_geteuid(cr);
+       attr.gid = kauth_cred_getegid(cr);
+       attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type);
+
+       if ((error = v7fs_file_allocate(fs, &parent_node->inode,
+           cnp->cn_nameptr, &attr, &ino)))
+               goto unlock_exit;
+       /* Sync dirent size change. */
+       uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode));
+
+       if ((error = v7fs_vget(mp, ino, a->a_vpp))) {
+               DPRINTF("can't get vnode.\n");
+       }
+       struct v7fs_node *newnode = (*a->a_vpp)->v_data;
+       newnode->update_ctime = true;
+       newnode->update_mtime = true;
+       newnode->update_atime = true;
+
+unlock_exit:
+       vput(dvp);
+
+       return error;
+}
+
+int
+v7fs_rmdir(void *v)
+{
+       struct vop_rmdir_args /* {
+                                struct vnode           *a_dvp;
+                                struct vnode           *a_vp;
+                                struct componentname   *a_cnp;
+                                } */ *a = v;
+       struct vnode *vp = a->a_vp;
+       struct vnode *dvp = a->a_dvp;
+       struct v7fs_node *parent_node = dvp->v_data;
+       struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
+       struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode;
+       struct v7fs_self *fs = v7fsmount->core;
+       int error = 0;
+
+       DPRINTF("delete %s\n", a->a_cnp->cn_nameptr);
+
+       KDASSERT(vp->v_type == VDIR);
+
+       if ((error = v7fs_file_deallocate(fs, &parent_node->inode,
+           a->a_cnp->cn_nameptr))) {
+               DPRINTF("v7fs_directory_deallocate failed.\n");
+               goto out;
+       }
+       uvm_vnp_setsize(vp, 0);
+       /* Sync dirent size change. */
+       uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode));
+       /* This inode is no longer used. -> v7fs_inactive */
+       memset(inode, 0, sizeof(*inode));
+out:
+       vput(vp);
+       vput(dvp);
+
+       return error;
+}
+
+struct v7fs_readdir_arg {
+       struct dirent *dp;
+       struct uio *uio;
+       int start;
+       int end;
+       int cnt;
+};
+static int readdir_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t);
+
+int
+readdir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz)
+{
+       struct v7fs_readdir_arg *p = (struct v7fs_readdir_arg *)ctx;
+       struct v7fs_dirent *dir;
+       struct dirent *dp = p->dp;
+       struct v7fs_inode inode;
+       char filename[V7FS_NAME_MAX + 1];
+       int i, n;
+       int error = 0;
+       void *buf;
+
+       if (!(buf = scratch_read(fs, blk)))
+               return EIO;
+       dir = (struct v7fs_dirent *)buf;
+
+       n = sz / sizeof(*dir);
+
+       for (i = 0; (i < n) && (p->cnt < p->end); i++, dir++, p->cnt++) {
+               if (p->cnt < p->start)
+                       continue;
+
+               if ((error = v7fs_inode_load(fs, &inode, dir->inode_number)))
+                       break;
+
+               v7fs_dirent_filename(filename, dir->name);
+
+               DPRINTF("inode=%d name=%s %s\n", dir->inode_number, filename,
+                   v7fs_inode_isdir(&inode) ? "DIR" : "FILE");
+               memset(dp, 0, sizeof(*dp));
+               dp->d_fileno = dir->inode_number;
+               dp->d_type = v7fs_mode_to_d_type(inode.mode);
+               dp->d_namlen = strlen(filename);
+               strcpy(dp->d_name, filename);
+               dp->d_reclen = sizeof(*dp);
+               if ((error = uiomove(dp, dp->d_reclen, p->uio))) {
+                       DPRINTF("uiomove failed.\n");
+                       break;
+               }
+       }
+       scratch_free(fs, buf);
+
+       if (p->cnt == p->end)
+               return V7FS_ITERATOR_BREAK;
+
+       return error;
+}
+
+int
+v7fs_readdir(void *v)
+{
+       struct vop_readdir_args /* {
+                                  struct vnode *a_vp;
+                                  struct uio *a_uio;
+                                  kauth_cred_t a_cred;
+                                  int *a_eofflag;
+                                  off_t **a_cookies;
+                                  int *a_ncookies;
+                                  } */ *a = v;
+       struct uio *uio = a->a_uio;
+       struct vnode *vp = a->a_vp;
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_inode *inode = &v7node->inode;
+       struct v7fs_self *fs = v7node->v7fsmount->core;
+       struct dirent *dp;
+       int error;
+
+       DPRINTF("offset=%zu residue=%zu\n", uio->uio_offset, uio->uio_resid);
+
+       KDASSERT(vp->v_type == VDIR);
+       KDASSERT(uio->uio_offset >= 0);
+       KDASSERT(v7fs_inode_isdir(inode));
+
+       struct v7fs_readdir_arg arg;
+       arg.start = uio->uio_offset / sizeof(*dp);
+       arg.end = arg.start +  uio->uio_resid / sizeof(*dp);
+       if (arg.start == arg.end) {/* user buffer has not enuf space. */
+               DPRINTF("uio buffer too small\n");
+               return ENOMEM;
+       }
+       dp = kmem_zalloc(sizeof(*dp), KM_SLEEP);
+       arg.cnt = 0;
+       arg.dp = dp;
+       arg.uio = uio;
+
+       *a->a_eofflag = false;
+       error = v7fs_datablock_foreach(fs, inode, readdir_subr, &arg);
+       if (error == V7FS_ITERATOR_END) {
+               *a->a_eofflag = true;
+       }
+       if (error < 0)
+               error = 0;
+
+       kmem_free(dp, sizeof(*dp));
+
+       return error;
+}
+
+int
+v7fs_inactive(void *v)
+{
+       struct vop_inactive_args /* {
+                                   struct vnode *a_vp;
+                                   bool *a_recycle;
+                                   } */ *a = v;
+       struct vnode *vp = a->a_vp;
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_inode *inode = &v7node->inode;
+
+       DPRINTF("%p #%d\n", vp, inode->inode_number);
+       if (v7fs_inode_allocated(inode)) {
+               v7fs_update(vp, 0, 0, UPDATE_WAIT);
+               *a->a_recycle = false;
+       } else {
+               *a->a_recycle = true;
+       }
+
+       VOP_UNLOCK(vp);
+
+       return 0;
+}
+
+int
+v7fs_reclaim(void *v)
+{
+       /*This vnode is no longer referenced by kernel. */
+       extern struct pool v7fs_node_pool;
+       struct vop_reclaim_args /* {
+                                  struct vnode *a_vp;
+                                  } */ *a = v;
+       struct vnode *vp = a->a_vp;
+       struct v7fs_node *v7node = vp->v_data;
+
+       DPRINTF("%p #%d\n", vp, v7node->inode.inode_number);
+       mutex_enter(&mntvnode_lock);
+       LIST_REMOVE(v7node, link);
+       mutex_exit(&mntvnode_lock);
+       genfs_node_destroy(vp);
+       pool_put(&v7fs_node_pool, v7node);
+       vp->v_data = NULL;
+
+       return 0;
+}
+
+int
+v7fs_bmap(void *v)
+{
+       struct vop_bmap_args /* {
+                               struct vnode *a_vp;
+                               daddr_t  a_bn;
+                               struct vnode **a_vpp;
+                               daddr_t *a_bnp;
+                               int *a_runp;
+                               } */ *a = v;
+       struct vnode *vp = a->a_vp;
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_mount *v7fsmount = v7node->v7fsmount;
+       struct v7fs_self *fs = v7node->v7fsmount->core;
+       struct v7fs_inode *inode = &v7node->inode;
+       int error = 0;
+
+       DPRINTF("inode=%d offset=%zu %p\n", inode->inode_number, a->a_bn, vp);
+       DPRINTF("filesize: %d\n", inode->filesize);
+       if (!a->a_bnp)
+               return 0;
+
+       v7fs_daddr_t blk;
+       if (!(blk = v7fs_datablock_last(fs, inode,
+           (a->a_bn + 1) << V7FS_BSHIFT))) {
+               /* +1 converts block # to file offset. */
+               return ENOSPC;
+       }
+
+       *a->a_bnp = blk;
+
+       if (a->a_vpp)
+               *a->a_vpp = v7fsmount->devvp;
+       if (a->a_runp)
+               *a->a_runp = 0; /*XXX TODO */
+
+       DPRINTF("%d  %zu->%zu status=%d\n", inode->inode_number, a->a_bn,
+           *a->a_bnp, error);
+
+       return error;
+}
+
+int
+v7fs_strategy(void *v)
+{
+       struct vop_strategy_args /* {
+                                   struct vnode *a_vp;
+                                   struct buf *a_bp;
+                                   } */ *a = v;
+       struct buf *b = a->a_bp;
+       struct vnode *vp = a->a_vp;
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_mount *v7fsmount = v7node->v7fsmount;
+       int error;
+
+       DPRINTF("%p\n", vp);
+       KDASSERT(vp->v_type == VREG);
+       if (b->b_blkno == b->b_lblkno) {
+               error = VOP_BMAP(vp, b->b_lblkno, NULL, &b->b_blkno, NULL);
+               if (error) {
+                       b->b_error = error;
+                       biodone(b);
+                       return error;
+               }
+               if ((long)b->b_blkno == -1)
+                       clrbuf(b);
+       }
+       if ((long)b->b_blkno == -1) {
+               biodone(b);
+               return 0;
+       }
+
+       return VOP_STRATEGY(v7fsmount->devvp, b);
+}
+
+int
+v7fs_print(void *v)
+{
+       struct vop_print_args /* {
+                                struct vnode *a_vp;
+                                } */ *a = v;
+       struct v7fs_node *v7node = a->a_vp->v_data;
+
+       v7fs_inode_dump(&v7node->inode);
+
+       return 0;
+}
+
+int
+v7fs_advlock(void *v)
+{
+       struct vop_advlock_args /* {
+                                  struct vnode *a_vp;
+                                  void *a_id;
+                                  int a_op;
+                                  struct flock *a_fl;
+                                  int a_flags;
+                                  } */ *a = v;
+       struct v7fs_node *v7node = a->a_vp->v_data;
+
+       DPRINTF("op=%d\n", a->a_op);
+
+       return lf_advlock(a, &v7node->lockf,
+           v7fs_inode_filesize(&v7node->inode));
+}
+
+int
+v7fs_pathconf(void *v)
+{
+       struct vop_pathconf_args /* {
+                                   struct vnode *a_vp;
+                                   int a_name;
+                                   register_t *a_retval;
+                                   } */ *a = v;
+       int err = 0;
+
+       DPRINTF("%p\n", a->a_vp);
+
+       switch (a->a_name) {
+       case _PC_LINK_MAX:
+               *a->a_retval = V7FS_LINK_MAX;
+               break;
+       case _PC_NAME_MAX:
+               *a->a_retval = V7FS_NAME_MAX;
+               break;
+       case _PC_PATH_MAX:
+               *a->a_retval = V7FS_PATH_MAX;
+               break;
+       case _PC_CHOWN_RESTRICTED:
+               *a->a_retval = 1;
+               break;
+       case _PC_NO_TRUNC:
+               *a->a_retval = 0;
+               break;
+       case _PC_SYNC_IO:
+               *a->a_retval = 1;
+               break;
+       case _PC_FILESIZEBITS:
+               *a->a_retval = 30; /* ~1G */
+               break;
+       case _PC_SYMLINK_MAX:
+               *a->a_retval = V7FSBSD_MAXSYMLINKLEN;
+               break;
+       case _PC_2_SYMLINKS:
+               *a->a_retval = 1;
+               break;
+       default:
+               err = EINVAL;
+               break;
+       }
+
+       return err;
+}
+
+int
+v7fs_update(struct vnode *vp, const struct timespec *acc,
+    const struct timespec *mod, int flags)
+{
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_inode *inode = &v7node->inode;
+       struct v7fs_self *fs = v7node->v7fsmount->core;
+       bool update = false;
+
+       DPRINTF("%p %zu %d\n", vp, vp->v_size, v7fs_inode_filesize(inode));
+       KDASSERT(vp->v_size == v7fs_inode_filesize(inode));
+
+       if (v7node->update_atime) {
+               inode->atime = acc ? acc->tv_sec : time_second;
+               v7node->update_atime = false;
+               update = true;
+       }
+       if (v7node->update_ctime) {
+               inode->ctime = time_second;
+               v7node->update_ctime = false;
+               update = true;
+       }
+       if (v7node->update_mtime) {
+               inode->mtime = mod ? mod->tv_sec : time_second;
+               v7node->update_mtime = false;
+               update = true;
+       }
+
+       if (update)
+               v7fs_inode_writeback(fs, inode);
+
+       return 0;
+}
+
+int
+v7fs_symlink(void *v)
+{
+       struct vop_symlink_args /* {
+                                  struct vnode         *a_dvp;
+                                  struct vnode         **a_vpp;
+                                  struct componentname *a_cnp;
+                                  struct vattr         *a_vap;
+                                  char                 *a_target;
+                                  } */ *a = v;
+       struct v7fs_node *parent_node = a->a_dvp->v_data;
+       struct v7fs_mount *v7fsmount = parent_node->v7fsmount;
+       struct v7fs_self *fs = v7fsmount->core;
+       struct vattr *va = a->a_vap;
+       kauth_cred_t cr = a->a_cnp->cn_cred;
+       struct componentname *cnp = a->a_cnp;
+       struct v7fs_fileattr attr;
+       v7fs_ino_t ino;
+       const char *from = a->a_target;
+       const char *to = cnp->cn_nameptr;
+       size_t len = strlen(from) + 1;
+       int error = 0;
+
+       if (len > V7FS_BSIZE) { /* limited to 512byte pathname */
+               DPRINTF("too long pathname.");
+               return ENAMETOOLONG;
+       }
+
+       memset(&attr, 0, sizeof(attr));
+       attr.uid = kauth_cred_geteuid(cr);
+       attr.gid = kauth_cred_getegid(cr);
+       attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type);
+
+       if ((error = v7fs_file_allocate
+               (fs, &parent_node->inode, to, &attr, &ino))) {
+               goto unlock_exit;
+       }
+       /* Sync dirent size change. */
+       uvm_vnp_setsize(a->a_dvp, v7fs_inode_filesize(&parent_node->inode));
+
+       /* Get myself vnode. */
+       if ((error = v7fs_vget(v7fsmount->mountp, ino, a->a_vpp))) {
+               DPRINTF("can't get vnode.\n");
+       }
+
+       struct v7fs_node *newnode = (*a->a_vpp)->v_data;
+       struct v7fs_inode *p = &newnode->inode;
+       v7fs_file_symlink(fs, p, from);
+       uvm_vnp_setsize(*a->a_vpp, v7fs_inode_filesize(p));
+
+       newnode->update_ctime = true;
+       newnode->update_mtime = true;
+       newnode->update_atime = true;
+unlock_exit:
+       /* unlock parent directory */
+       vput(a->a_dvp);
+
+       return error;
+}
+
+int
+v7fs_readlink(void *v)
+{
+       struct vop_readlink_args /* {
+                                   struct vnode        *a_vp;
+                                   struct uio          *a_uio;
+                                   kauth_cred_t        a_cred;
+                                   } */ *a = v;
+       struct uio *uio = a->a_uio;
+       struct vnode *vp = a->a_vp;
+       struct v7fs_node *v7node = vp->v_data;
+       struct v7fs_inode *inode = &v7node->inode;
+       struct v7fs_self *fs = v7node->v7fsmount->core;
+       int error = 0;
+
+       KDASSERT(vp->v_type == VLNK);
+       KDASSERT(uio->uio_offset >= 0);
+       KDASSERT(v7fs_inode_islnk(inode));
+
+       v7fs_daddr_t blk = inode->addr[0];
+       void *buf;
+       if (!(buf = scratch_read(fs, blk))) {
+               error = EIO;
+               goto error_exit;
+       }
+
+       if ((error = uiomove(buf, strlen(buf), uio))) {
+               DPRINTF("uiomove failed.\n");
+       }
+       scratch_free(fs, buf);
+
+error_exit:
+       return error;
+}
index bd000bb5c88a2d24f9cb0b6a8d9b4603f219dbba..a0a8d69a45707f2f13b1a34913cab5d9369e7077 100644 (file)
@@ -15,7 +15,7 @@ SUBDIR= \
        \
        \
        link \
-       \
+       makefs \
        mtree \
        \
        \
diff --git a/usr.sbin/makefs/Makefile b/usr.sbin/makefs/Makefile
new file mode 100644 (file)
index 0000000..7522fc9
--- /dev/null
@@ -0,0 +1,37 @@
+#      $NetBSD: Makefile,v 1.36 2013/08/05 14:41:57 reinoud Exp $
+#
+
+WARNS?=        5
+
+.include <bsd.own.mk>
+
+PROG=  makefs
+SRCS=  cd9660.c chfs.c ffs.c v7fs.c msdos.c udf.c\
+       getid.c \
+       makefs.c misc.c \
+       pack_dev.c \
+       spec.c \
+       walk.c
+MAN=   makefs.8
+
+MKNODSRC=      ${NETBSDSRCDIR}/sbin/mknod
+MTREESRC=      ${NETBSDSRCDIR}/usr.sbin/mtree
+
+CPPFLAGS+=     -I${.CURDIR} -I${MKNODSRC} -I${MTREESRC} -DMAKEFS
+#CPPFLAGS+=    -DMSDOSFS_DEBUG
+.PATH:         ${MKNODSRC} ${MTREESRC}
+
+.include "${.CURDIR}/cd9660/Makefile.inc"
+.include "${.CURDIR}/chfs/Makefile.inc"
+.include "${.CURDIR}/ffs/Makefile.inc"
+.include "${.CURDIR}/v7fs/Makefile.inc"
+.include "${.CURDIR}/msdos/Makefile.inc"
+.include "${.CURDIR}/udf/Makefile.inc"
+
+.if !defined(HOSTPROG)
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+.endif
+
+.include <bsd.prog.mk>
+# DO NOT DELETE
diff --git a/usr.sbin/makefs/README b/usr.sbin/makefs/README
new file mode 100644 (file)
index 0000000..7779a06
--- /dev/null
@@ -0,0 +1,129 @@
+$NetBSD: README,v 1.5 2011/07/18 08:58:38 uch Exp $
+
+makefs - build a file system image from a directory tree
+
+NOTES:
+
+    *   This tool uses modified local copies of source found in other
+       parts of the tree.  This is intentional.
+
+    *  makefs is a work in progress, and subject to change.
+
+
+user overview:
+--------------
+
+makefs creates a file system image from a given directory tree.
+the following file system types can be built:
+       ffs     BSD fast file system
+       cd9660  ISO 9660 file system
+       v7fs    7th edition(V7) file system
+
+Support for the following file systems maybe be added in the future
+       ext2fs  Linux EXT2 file system
+       fat     MS-DOS `FAT' file system (FAT12, FAT16, FAT32)
+
+Various file system independent parameters and contraints can be
+specified, such as:
+       - minimum file system size (in KB)
+       - maximum file system size (in KB)
+       - free inodes
+       - free blocks (in KB)
+       - mtree(8) specification file containing permissions and ownership
+         to use in image, overridding the settings in the directory tree
+       - file containing list of files to specifically exclude or include
+       - fnmatch(3) pattern of filenames to exclude or include
+       - endianness of target file system
+
+File system specific parameters can be given as well, with a command
+line option such as "-o fsspeccific-options,comma-separated".
+For example, ffs would allow tuning of:
+       - block & fragment size
+       - cylinder groups
+       - number of blocks per inode
+       - minimum free space
+
+Other file systems might have controls on how to "munge" file names to
+fit within the constraints of the target file system.
+
+Exit codes:
+       0       all ok
+       1       fatal error
+       2       some files couldn't be added during image creation
+               (bad perms, missing file, etc). image will continue
+               to be made
+
+
+Implementation overview:
+------------------------
+
+The implementation must allow for easy addition of extra file systems
+with minimal changes to the file system independent sections.
+
+The main program will:
+       - parse the options, including calling fs-specific routines to
+         validate fs-specific options
+       - walk the tree, building up a data structure which represents
+         the tree to stuff into the image. The structure will
+         probably be a similar tree to what mtree(8) uses internally;
+         a linked list of entries per directory with a child pointer
+         to children of directories. ".." won't be stored in the list;
+         the fs-specific tree walker should add this if required by the fs. 
+         this builder have the smarts to handle hard links correctly.
+       - (optionally) Change the permissions in the tree according to
+         the mtree(8) specfile
+       - Call an fs-specific routine to build the image based on the
+         data structures.
+
+Each fs-specific module should have the following external interfaces:
+
+    prepare_options    optional file system specific defaults that need to be
+                       setup before parsing fs-specific options.
+
+    parse_options      parse the string for fs-specific options, feeding
+                       errors back to the user as appropriate
+
+    cleanup_options    optional file system specific data that need to be
+                       cleaned up when done with this filesystem.
+
+    make_fs            take the data structures representing the
+                       directory tree and fs parameters,
+                       validate that the parameters are valid
+                       (e.g, the requested image will be large enough), 
+                       create the image, and
+                       populate the image
+
+prepare_options and cleanup_options are optional and can be NULL.
+
+NOTE: All file system specific options are referenced via the fs_specific
+pointer from the fsinfo_t strucutre. It is up to the filesystem to allocate
+and free any data needed for this via the prepare and cleanup callbacks.
+
+Each fs-specific module will need to add it's routines to the dispatch array
+in makefs.c and add prototypes for these to makefs.h
+
+All other implementation details should not need to change any of the
+generic code.
+
+ffs implementation
+------------------
+
+In the ffs case, we can leverage off sbin/newfs/mkfs.c to actually build
+the image. When building and populating the image, the implementation
+can be greatly simplified if some assumptions are made:
+       - the total required size (in blocks and inodes) is determined
+         as part of the validation phase
+       - a "file" (including a directory) has a known size, so
+         support for growing a file is not necessary
+
+Two underlying primitives are provided:
+       make_inode      create an inode, returning the inode number
+
+       write_file      write file (from memory if DIR, file descriptor
+                       if FILE or SYMLINK), referencing given inode.
+                       it is smart enough to know if a short symlink
+                       can be stuffed into the inode, etc.
+
+When creating a directory, the directory entries in the previously
+built tree data structure is scanned and built in memory so it can
+be written entirely as a single write_file() operation.
diff --git a/usr.sbin/makefs/TODO b/usr.sbin/makefs/TODO
new file mode 100644 (file)
index 0000000..dfd306a
--- /dev/null
@@ -0,0 +1,41 @@
+$NetBSD: TODO,v 1.7 2007/12/10 23:54:35 dyoung Exp $
+
+todo
+----
+
+- read files from multiple directories with or without root
+  specification, e.g., makefs -t cd9660 output.iso dir1 root2=dir2
+  dir3 root4=dir4
+
+- display block numbers for a given file (e.g, /boot)
+
+- finish makefs.8
+
+- testing
+
+- even more testing
+
+- add support for converting a tar file (instead of a directory tree);
+  suggested by kpneal@pobox.com
+
+
+outstanding bugs
+----------------
+
+- size estimation is still out (need to take into account indirect blocks!)
+
+- parameter checking when density is rather high or low.
+
+- filling up a file system (running out of inodes or whatever)
+  doesn't do the right thing.
+
+
+discuss
+-------
+
+- consider replacing ffs_balloc() et al with own code that doesn't
+  need hacked-up buf.c code
+
+- whacking on newfs/mkfs.c to allow .PATH-ing directly into makefs(8).
+  this would involve passing all of mkfs()'s parameters in a single
+  struct rather than a lot of global vars, etc.
diff --git a/usr.sbin/makefs/cd9660.c b/usr.sbin/makefs/cd9660.c
new file mode 100644 (file)
index 0000000..c449e0c
--- /dev/null
@@ -0,0 +1,2148 @@
+/*     $NetBSD: cd9660.c,v 1.47 2013/10/24 14:01:01 apb Exp $  */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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) 1982, 1986, 1989, 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.
+ *
+  */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#else
+#include <sys/mount.h>
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: cd9660.c,v 1.47 2013/10/24 14:01:01 apb Exp $");
+#endif  /* !__lint */
+
+#include <string.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "cd9660/iso9660_rrip.h"
+#include "cd9660/cd9660_archimedes.h"
+
+/*
+ * Global variables
+ */
+
+static void cd9660_finalize_PVD(iso9660_disk *);
+static cd9660node *cd9660_allocate_cd9660node(void);
+static void cd9660_set_defaults(iso9660_disk *);
+static int cd9660_arguments_set_string(const char *, const char *, int,
+    char, char *);
+static void cd9660_populate_iso_dir_record(
+    struct _iso_directory_record_cd9660 *, u_char, u_char, u_char,
+    const char *);
+static void cd9660_setup_root_node(iso9660_disk *);
+static int cd9660_setup_volume_descriptors(iso9660_disk *);
+#if 0
+static int cd9660_fill_extended_attribute_record(cd9660node *);
+#endif
+static void cd9660_sort_nodes(cd9660node *);
+static int cd9660_translate_node_common(iso9660_disk *, cd9660node *);
+static int cd9660_translate_node(iso9660_disk *, fsnode *, cd9660node *);
+static int cd9660_compare_filename(const char *, const char *);
+static void cd9660_sorted_child_insert(cd9660node *, cd9660node *);
+static int cd9660_handle_collisions(iso9660_disk *, cd9660node *, int);
+static cd9660node *cd9660_rename_filename(iso9660_disk *, cd9660node *, int,
+    int);
+static void cd9660_copy_filenames(iso9660_disk *, cd9660node *);
+static void cd9660_sorting_nodes(cd9660node *);
+static int cd9660_count_collisions(cd9660node *);
+static cd9660node *cd9660_rrip_move_directory(iso9660_disk *, cd9660node *);
+static int cd9660_add_dot_records(iso9660_disk *, cd9660node *);
+
+static void cd9660_convert_structure(iso9660_disk *, fsnode *, cd9660node *, int,
+    int *, int *);
+static void cd9660_free_structure(cd9660node *);
+static int cd9660_generate_path_table(iso9660_disk *);
+static int cd9660_level1_convert_filename(iso9660_disk *, const char *, char *,
+    int);
+static int cd9660_level2_convert_filename(iso9660_disk *, const char *, char *,
+    int);
+#if 0
+static int cd9660_joliet_convert_filename(iso9660_disk *, const char *, char *,
+    int);
+#endif
+static int cd9660_convert_filename(iso9660_disk *, const char *, char *, int);
+static void cd9660_populate_dot_records(iso9660_disk *, cd9660node *);
+static int64_t cd9660_compute_offsets(iso9660_disk *, cd9660node *, int64_t);
+#if 0
+static int cd9660_copy_stat_info(cd9660node *, cd9660node *, int);
+#endif
+static cd9660node *cd9660_create_virtual_entry(iso9660_disk *, const char *,
+    cd9660node *, int, int);
+static cd9660node *cd9660_create_file(iso9660_disk *, const char *,
+    cd9660node *, cd9660node *);
+static cd9660node *cd9660_create_directory(iso9660_disk *, const char *,
+    cd9660node *, cd9660node *);
+static cd9660node *cd9660_create_special_directory(iso9660_disk *, u_char,
+    cd9660node *);
+static int  cd9660_add_generic_bootimage(iso9660_disk *, const char *);
+
+
+/*
+ * Allocate and initalize a cd9660node
+ * @returns struct cd9660node * Pointer to new node, or NULL on error
+ */
+static cd9660node *
+cd9660_allocate_cd9660node(void)
+{
+       cd9660node *temp = ecalloc(1, sizeof(*temp));
+       TAILQ_INIT(&temp->cn_children);
+       temp->parent = temp->dot_record = temp->dot_dot_record = NULL;
+       temp->ptnext = temp->ptprev = temp->ptlast = NULL;
+       temp->node = NULL;
+       temp->isoDirRecord = NULL;
+       temp->isoExtAttributes = NULL;
+       temp->rr_real_parent = temp->rr_relocated = NULL;
+       temp->su_tail_data = NULL;
+       return temp;
+}
+
+int cd9660_defaults_set = 0;
+
+/**
+* Set default values for cd9660 extension to makefs
+*/
+static void
+cd9660_set_defaults(iso9660_disk *diskStructure)
+{
+       /*Fix the sector size for now, though the spec allows for other sizes*/
+       diskStructure->sectorSize = 2048;
+
+       /* Set up defaults in our own structure */
+       diskStructure->verbose_level = 0;
+       diskStructure->keep_bad_images = 0;
+       diskStructure->follow_sym_links = 0;
+       diskStructure->isoLevel = 2;
+
+       diskStructure->rock_ridge_enabled = 0;
+       diskStructure->rock_ridge_renamed_dir_name = 0;
+       diskStructure->rock_ridge_move_count = 0;
+       diskStructure->rr_moved_dir = 0;
+
+       diskStructure->archimedes_enabled = 0;
+       diskStructure->chrp_boot = 0;
+
+       diskStructure->include_padding_areas = 1;
+
+       /* Spec breaking functionality */
+       diskStructure->allow_deep_trees =
+           diskStructure->allow_start_dot =
+           diskStructure->allow_max_name =
+           diskStructure->allow_illegal_chars =
+           diskStructure->allow_lowercase =
+           diskStructure->allow_multidot =
+           diskStructure->omit_trailing_period = 0;
+
+       /* Make sure the PVD is clear */
+       memset(&diskStructure->primaryDescriptor, 0, 2048);
+
+       memset(diskStructure->primaryDescriptor.publisher_id,   0x20,128);
+       memset(diskStructure->primaryDescriptor.preparer_id,    0x20,128);
+       memset(diskStructure->primaryDescriptor.application_id, 0x20,128);
+       memset(diskStructure->primaryDescriptor.copyright_file_id, 0x20,37);
+       memset(diskStructure->primaryDescriptor.abstract_file_id, 0x20,37);
+       memset(diskStructure->primaryDescriptor.bibliographic_file_id, 0x20,37);
+
+       strcpy(diskStructure->primaryDescriptor.system_id,"NetBSD");
+
+       cd9660_defaults_set = 1;
+
+       /* Boot support: Initially disabled */
+       diskStructure->has_generic_bootimage = 0;
+       diskStructure->generic_bootimage = NULL;
+
+       diskStructure->boot_image_directory = 0;
+       /*memset(diskStructure->boot_descriptor, 0, 2048);*/
+
+       diskStructure->is_bootable = 0;
+       TAILQ_INIT(&diskStructure->boot_images);
+       LIST_INIT(&diskStructure->boot_entries);
+}
+
+void
+cd9660_prep_opts(fsinfo_t *fsopts)
+{
+       iso9660_disk *diskStructure = ecalloc(1, sizeof(*diskStructure));
+
+#define OPT_STR(letter, name, desc)  \
+       { letter, name, NULL, OPT_STRBUF, 0, 0, desc }
+
+#define OPT_NUM(letter, name, field, min, max, desc) \
+       { letter, name, &diskStructure->field, \
+         sizeof(diskStructure->field) == 8 ? OPT_INT64 : \
+         (sizeof(diskStructure->field) == 4 ? OPT_INT32 : \
+         (sizeof(diskStructure->field) == 2 ? OPT_INT16 : OPT_INT8)), \
+         min, max, desc }
+
+#define OPT_BOOL(letter, name, field, desc) \
+       OPT_NUM(letter, name, field, 0, 1, desc)
+
+       const option_t cd9660_options[] = {
+               OPT_NUM('l', "isolevel", isoLevel,
+                   1, 3, "ISO Level"),
+               OPT_NUM('v', "verbose",  verbose_level,
+                   0, 2, "Turns on verbose output"),
+
+               OPT_BOOL('h', "help", displayHelp,
+                   "Show help message"),
+               OPT_BOOL('S', "follow-symlinks", follow_sym_links,
+                   "Resolve symlinks in pathnames"),
+               OPT_BOOL('R', "rockridge", rock_ridge_enabled,
+                   "Enable Rock-Ridge extensions"),
+               OPT_BOOL('C', "chrp-boot", chrp_boot,
+                   "Enable CHRP boot"),
+               OPT_BOOL('K', "keep-bad-images", keep_bad_images,
+                   "Keep bad images"),
+               OPT_BOOL('D', "allow-deep-trees", allow_deep_trees,
+                   "Allow trees more than 8 levels"),
+               OPT_BOOL('a', "allow-max-name", allow_max_name,
+                   "Allow 37 char filenames (unimplemented)"),
+               OPT_BOOL('i', "allow-illegal-chars", allow_illegal_chars,
+                   "Allow illegal characters in filenames"),
+               OPT_BOOL('D', "allow-multidot", allow_multidot,
+                   "Allow multiple periods in filenames"),
+               OPT_BOOL('o', "omit-trailing-period", omit_trailing_period,
+                   "Omit trailing periods in filenames"),
+               OPT_BOOL('\0', "allow-lowercase", allow_lowercase,
+                   "Allow lowercase characters in filenames"),
+               OPT_BOOL('\0', "archimedes", archimedes_enabled,
+                   "Enable Archimedes structure"),
+               OPT_BOOL('\0', "no-trailing-padding", include_padding_areas,
+                   "Include padding areas"),
+
+               OPT_STR('A', "applicationid", "Application Identifier"),
+               OPT_STR('P', "publisher", "Publisher Identifier"),
+               OPT_STR('p', "preparer", "Preparer Identifier"),
+               OPT_STR('L', "label", "Disk Label"),
+               OPT_STR('V', "volumeid", "Volume Set Identifier"),
+               OPT_STR('B', "bootimage", "Boot image parameter"),
+               OPT_STR('G', "generic-bootimage", "Generic boot image param"),
+               OPT_STR('\0', "bootimagedir", "Boot image directory"),
+               OPT_STR('\0', "no-emul-boot", "No boot emulation"),
+               OPT_STR('\0', "no-boot", "No boot support"),
+               OPT_STR('\0', "hard-disk-boot", "Boot from hard disk"),
+               OPT_STR('\0', "boot-load-segment", "Boot load segment"),
+
+               { .name = NULL }
+       };
+
+       fsopts->fs_specific = diskStructure;
+       fsopts->fs_options = copy_opts(cd9660_options);
+
+       cd9660_set_defaults(diskStructure);
+}
+
+void
+cd9660_cleanup_opts(fsinfo_t *fsopts)
+{
+       free(fsopts->fs_specific);
+       free(fsopts->fs_options);
+}
+
+static int
+cd9660_arguments_set_string(const char *val, const char *fieldtitle, int length,
+                           char testmode, char * dest)
+{
+       int len, test;
+
+       if (val == NULL)
+               warnx("error: The %s requires a string argument", fieldtitle);
+       else if ((len = strlen(val)) <= length) {
+               if (testmode == 'd')
+                       test = cd9660_valid_d_chars(val);
+               else
+                       test = cd9660_valid_a_chars(val);
+               if (test) {
+                       memcpy(dest, val, len);
+                       if (test == 2)
+                               cd9660_uppercase_characters(dest, len);
+                       return 1;
+               } else
+                       warnx("error: The %s must be composed of "
+                             "%c-characters", fieldtitle, testmode);
+       } else
+               warnx("error: The %s must be at most 32 characters long",
+                   fieldtitle);
+       return 0;
+}
+
+/*
+ * Command-line parsing function
+ */
+
+int
+cd9660_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+       int     rv, i;
+       iso9660_disk *diskStructure = fsopts->fs_specific;
+       option_t *cd9660_options = fsopts->fs_options;
+       char buf[1024];
+       const char *name, *desc;
+
+       assert(option != NULL);
+
+       if (debug & DEBUG_FS_PARSE_OPTS)
+               printf("cd9660_parse_opts: got `%s'\n", option);
+
+       i = set_option(cd9660_options, option, buf, sizeof(buf));
+       if (i == -1)
+               return 0;
+
+       if (cd9660_options[i].name == NULL)
+               abort();
+
+
+       name = cd9660_options[i].name;
+       desc = cd9660_options[i].desc;
+       switch (cd9660_options[i].letter) {
+       case 'h':
+       case 'S':
+               rv = 0; /* this is not handled yet */
+               break;
+       case 'L':
+               rv = cd9660_arguments_set_string(buf, desc, 32, 'd',
+                   diskStructure->primaryDescriptor.volume_id);
+               break;
+       case 'A':
+               rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
+                   diskStructure->primaryDescriptor.application_id);
+               break;
+       case 'P':
+               rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
+                   diskStructure->primaryDescriptor.publisher_id);
+               break;
+       case 'p':
+               rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
+                   diskStructure->primaryDescriptor.preparer_id);
+               break;
+       case 'V':
+               rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
+                   diskStructure->primaryDescriptor.volume_set_id);
+               break;
+       /* Boot options */
+       case 'B':
+               if (buf[0] == '\0') {
+                       warnx("The Boot Image parameter requires a valid boot"
+                       " information string");
+                       rv = 0;
+               } else
+                       rv = cd9660_add_boot_disk(diskStructure, buf);
+               break;
+       case 'G':
+               if (buf[0] == '\0') {
+                       warnx("The Generic Boot Image parameter requires a"
+                           " valid boot information string");
+                       rv = 0;
+               } else
+                       rv = cd9660_add_generic_bootimage(diskStructure, buf);
+               break;
+       default:
+               if (strcmp(name, "bootimagedir") == 0) {
+                       /*
+                        * XXXfvdl this is unused.
+                        */
+                       if (buf[0] == '\0') {
+                               warnx("The Boot Image Directory parameter"
+                                " requires a directory name\n");
+                               rv = 0;
+                       } else {
+                               diskStructure->boot_image_directory =
+                                    emalloc(strlen(buf) + 1);
+                               /* BIG TODO: Add the max length function here */
+                               rv = cd9660_arguments_set_string(buf, desc, 12,
+                                   'd', diskStructure->boot_image_directory);
+                       }
+               } else if (strcmp(name, "no-emul-boot") == 0 ||
+                   strcmp(name, "no-boot") == 0 ||
+                   strcmp(name, "hard-disk-boot") == 0) {
+                       /* RRIP */
+                       cd9660_eltorito_add_boot_option(diskStructure, name, 0);
+                       rv = 1;
+               } else if (strcmp(name, "boot-load-segment") == 0) {
+                       if (buf[0] == '\0') {
+                               warnx("Option `%s' doesn't contain a value",
+                                   name);
+                               rv = 0;
+                       } else {
+                               cd9660_eltorito_add_boot_option(diskStructure,
+                                   name, buf);
+                               rv = 1;
+                       }
+               } else
+                       rv = 1;
+       }
+       return rv;
+}
+
+/*
+ * Main function for cd9660_makefs
+ * Builds the ISO image file
+ * @param const char *image The image filename to create
+ * @param const char *dir The directory that is being read
+ * @param struct fsnode *root The root node of the filesystem tree
+ * @param struct fsinfo_t *fsopts Any options
+ */
+void
+cd9660_makefs(const char *image, const char *dir, fsnode *root,
+             fsinfo_t *fsopts)
+{
+       int64_t startoffset;
+       int numDirectories;
+       uint64_t pathTableSectors;
+       int64_t firstAvailableSector;
+       int64_t totalSpace;
+       int error;
+       cd9660node *real_root;
+       iso9660_disk *diskStructure = fsopts->fs_specific;
+
+       if (diskStructure->verbose_level > 0)
+               printf("cd9660_makefs: ISO level is %i\n",
+                   diskStructure->isoLevel);
+       if (diskStructure->isoLevel < 2 &&
+           diskStructure->allow_multidot)
+               errx(1, "allow-multidot requires iso level of 2\n");
+
+       assert(image != NULL);
+       assert(dir != NULL);
+       assert(root != NULL);
+
+       if (diskStructure->displayHelp) {
+               /*
+                * Display help here - probably want to put it in
+                * a separate function
+                */
+               return;
+       }
+
+       if (diskStructure->verbose_level > 0)
+               printf("cd9660_makefs: image %s directory %s root %p\n",
+                   image, dir, root);
+
+       /* Set up some constants. Later, these will be defined with options */
+
+       /* Counter needed for path tables */
+       numDirectories = 0;
+
+       /* Convert tree to our own format */
+       /* Actually, we now need to add the REAL root node, at level 0 */
+
+       real_root = cd9660_allocate_cd9660node();
+       real_root->isoDirRecord = emalloc(sizeof(*real_root->isoDirRecord));
+       /* Leave filename blank for root */
+       memset(real_root->isoDirRecord->name, 0,
+           ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+
+       real_root->level = 0;
+       diskStructure->rootNode = real_root;
+       real_root->type = CD9660_TYPE_DIR;
+       error = 0;
+       real_root->node = root;
+       cd9660_convert_structure(diskStructure, root, real_root, 1,
+           &numDirectories, &error);
+
+       if (TAILQ_EMPTY(&real_root->cn_children)) {
+               errx(1, "cd9660_makefs: converted directory is empty. "
+                       "Tree conversion failed\n");
+       } else if (error != 0) {
+               errx(1, "cd9660_makefs: tree conversion failed\n");
+       } else {
+               if (diskStructure->verbose_level > 0)
+                       printf("cd9660_makefs: tree converted\n");
+       }
+
+       /* Add the dot and dot dot records */
+       cd9660_add_dot_records(diskStructure, real_root);
+
+       cd9660_setup_root_node(diskStructure);
+
+       if (diskStructure->verbose_level > 0)
+               printf("cd9660_makefs: done converting tree\n");
+
+       /* non-SUSP extensions */
+       if (diskStructure->archimedes_enabled)
+               archimedes_convert_tree(diskStructure->rootNode);
+
+       /* Rock ridge / SUSP init pass */
+       if (diskStructure->rock_ridge_enabled) {
+               cd9660_susp_initialize(diskStructure, diskStructure->rootNode,
+                   diskStructure->rootNode, NULL);
+       }
+
+       /* Build path table structure */
+       diskStructure->pathTableLength = cd9660_generate_path_table(
+           diskStructure);
+
+       pathTableSectors = CD9660_BLOCKS(diskStructure->sectorSize,
+               diskStructure->pathTableLength);
+
+       firstAvailableSector = cd9660_setup_volume_descriptors(diskStructure);
+       if (diskStructure->is_bootable) {
+               firstAvailableSector = cd9660_setup_boot(diskStructure,
+                   firstAvailableSector);
+               if (firstAvailableSector < 0)
+                       errx(1, "setup_boot failed");
+       }
+       /* LE first, then BE */
+       diskStructure->primaryLittleEndianTableSector = firstAvailableSector;
+       diskStructure->primaryBigEndianTableSector =
+               diskStructure->primaryLittleEndianTableSector + pathTableSectors;
+
+       /* Set the secondary ones to -1, not going to use them for now */
+       diskStructure->secondaryBigEndianTableSector = -1;
+       diskStructure->secondaryLittleEndianTableSector = -1;
+
+       diskStructure->dataFirstSector =
+           diskStructure->primaryBigEndianTableSector + pathTableSectors;
+       if (diskStructure->verbose_level > 0)
+               printf("cd9660_makefs: Path table conversion complete. "
+                      "Each table is %i bytes, or %" PRIu64 " sectors.\n",
+                   diskStructure->pathTableLength, pathTableSectors);
+
+       startoffset = diskStructure->sectorSize*diskStructure->dataFirstSector;
+
+       totalSpace = cd9660_compute_offsets(diskStructure, real_root, startoffset);
+
+       diskStructure->totalSectors = diskStructure->dataFirstSector +
+               CD9660_BLOCKS(diskStructure->sectorSize, totalSpace);
+
+       /* Disabled until pass 1 is done */
+       if (diskStructure->rock_ridge_enabled) {
+               diskStructure->susp_continuation_area_start_sector =
+                   diskStructure->totalSectors;
+               diskStructure->totalSectors +=
+                   CD9660_BLOCKS(diskStructure->sectorSize,
+                       diskStructure->susp_continuation_area_size);
+               cd9660_susp_finalize(diskStructure, diskStructure->rootNode);
+       }
+
+
+       cd9660_finalize_PVD(diskStructure);
+
+       /* Add padding sectors, just for testing purposes right now */
+       /* diskStructure->totalSectors+=150; */
+
+       /* Debugging output */
+       if (diskStructure->verbose_level > 0) {
+               printf("cd9660_makefs: Sectors 0-15 reserved\n");
+               printf("cd9660_makefs: Primary path tables starts in sector %"
+                   PRId64 "\n", diskStructure->primaryLittleEndianTableSector);
+               printf("cd9660_makefs: File data starts in sector %"
+                   PRId64 "\n", diskStructure->dataFirstSector);
+               printf("cd9660_makefs: Total sectors: %"
+                   PRId64 "\n", diskStructure->totalSectors);
+       }
+
+       /*
+        * Add padding sectors at the end
+        * TODO: Clean this up and separate padding
+        */
+       if (diskStructure->include_padding_areas)
+               diskStructure->totalSectors += 150;
+
+       cd9660_write_image(diskStructure, image);
+
+       if (diskStructure->verbose_level > 1) {
+               debug_print_volume_descriptor_information(diskStructure);
+               debug_print_tree(diskStructure, real_root, 0);
+               debug_print_path_tree(real_root);
+       }
+
+       /* Clean up data structures */
+       cd9660_free_structure(real_root);
+
+       if (diskStructure->verbose_level > 0)
+               printf("cd9660_makefs: done\n");
+}
+
+/* Generic function pointer - implement later */
+typedef int (*cd9660node_func)(cd9660node *);
+
+static void
+cd9660_finalize_PVD(iso9660_disk *diskStructure)
+{
+       time_t tim;
+
+       /* root should be a fixed size of 34 bytes since it has no name */
+       memcpy(diskStructure->primaryDescriptor.root_directory_record,
+               diskStructure->rootNode->dot_record->isoDirRecord, 34);
+
+       /* In RRIP, this might be longer than 34 */
+       diskStructure->primaryDescriptor.root_directory_record[0] = 34;
+
+       /* Set up all the important numbers in the PVD */
+       cd9660_bothendian_dword(diskStructure->totalSectors,
+           (unsigned char *)diskStructure->primaryDescriptor.volume_space_size);
+       cd9660_bothendian_word(1,
+           (unsigned char *)diskStructure->primaryDescriptor.volume_set_size);
+       cd9660_bothendian_word(1,
+           (unsigned char *)
+               diskStructure->primaryDescriptor.volume_sequence_number);
+       cd9660_bothendian_word(diskStructure->sectorSize,
+           (unsigned char *)
+               diskStructure->primaryDescriptor.logical_block_size);
+       cd9660_bothendian_dword(diskStructure->pathTableLength,
+           (unsigned char *)diskStructure->primaryDescriptor.path_table_size);
+
+       cd9660_731(diskStructure->primaryLittleEndianTableSector,
+               (u_char *)diskStructure->primaryDescriptor.type_l_path_table);
+       cd9660_732(diskStructure->primaryBigEndianTableSector,
+               (u_char *)diskStructure->primaryDescriptor.type_m_path_table);
+
+       diskStructure->primaryDescriptor.file_structure_version[0] = 1;
+
+       /* Pad all strings with spaces instead of nulls */
+       cd9660_pad_string_spaces(diskStructure->primaryDescriptor.volume_id, 32);
+       cd9660_pad_string_spaces(diskStructure->primaryDescriptor.system_id, 32);
+       cd9660_pad_string_spaces(diskStructure->primaryDescriptor.volume_set_id,
+           128);
+       cd9660_pad_string_spaces(diskStructure->primaryDescriptor.publisher_id,
+           128);
+       cd9660_pad_string_spaces(diskStructure->primaryDescriptor.preparer_id,
+           128);
+       cd9660_pad_string_spaces(diskStructure->primaryDescriptor.application_id,
+           128);
+       cd9660_pad_string_spaces(
+           diskStructure->primaryDescriptor.copyright_file_id, 37);
+       cd9660_pad_string_spaces(
+               diskStructure->primaryDescriptor.abstract_file_id, 37);
+       cd9660_pad_string_spaces(
+               diskStructure->primaryDescriptor.bibliographic_file_id, 37);
+
+       /* Setup dates */
+       time(&tim);
+       cd9660_time_8426(
+           (unsigned char *)diskStructure->primaryDescriptor.creation_date,
+           tim);
+       cd9660_time_8426(
+           (unsigned char *)diskStructure->primaryDescriptor.modification_date,
+           tim);
+
+       /*
+       cd9660_set_date(diskStructure->primaryDescriptor.expiration_date, now);
+       */
+       memset(diskStructure->primaryDescriptor.expiration_date, '0' ,16);
+       diskStructure->primaryDescriptor.expiration_date[16] = 0;
+
+       cd9660_time_8426(
+           (unsigned char *)diskStructure->primaryDescriptor.effective_date,
+           tim);
+}
+
+static void
+cd9660_populate_iso_dir_record(struct _iso_directory_record_cd9660 *record,
+                              u_char ext_attr_length, u_char flags,
+                              u_char name_len, const char * name)
+{
+       record->ext_attr_length[0] = ext_attr_length;
+       record->flags[0] = ISO_FLAG_CLEAR | flags;
+       record->file_unit_size[0] = 0;
+       record->interleave[0] = 0;
+       cd9660_bothendian_word(1, record->volume_sequence_number);
+       record->name_len[0] = name_len;
+       memset(record->name, '\0', sizeof (record->name));
+       memcpy(record->name, name, name_len);
+       record->length[0] = 33 + name_len;
+
+       /* Todo : better rounding */
+       record->length[0] += (record->length[0] & 1) ? 1 : 0;
+}
+
+static void
+cd9660_setup_root_node(iso9660_disk *diskStructure)
+{
+       cd9660_populate_iso_dir_record(diskStructure->rootNode->isoDirRecord,
+           0, ISO_FLAG_DIRECTORY, 1, "\0");
+
+}
+
+/*********** SUPPORT FUNCTIONS ***********/
+static int
+cd9660_setup_volume_descriptors(iso9660_disk *diskStructure)
+{
+       /* Boot volume descriptor should come second */
+       int sector = 16;
+       /* For now, a fixed 2 : PVD and terminator */
+       volume_descriptor *temp, *t;
+
+       /* Set up the PVD */
+       temp = emalloc(sizeof(*temp));
+       temp->volumeDescriptorData =
+          (unsigned char *)&diskStructure->primaryDescriptor;
+       temp->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_PVD;
+       temp->volumeDescriptorData[6] = 1;
+       temp->sector = sector;
+       memcpy(temp->volumeDescriptorData + 1,
+           ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+       diskStructure->firstVolumeDescriptor = temp;
+
+       sector++;
+       /* Set up boot support if enabled. BVD must reside in sector 17 */
+       if (diskStructure->is_bootable) {
+               t = emalloc(sizeof(*t));
+               t->volumeDescriptorData = ecalloc(1, 2048);
+               temp->next = t;
+               temp = t;
+               t->sector = 17;
+               if (diskStructure->verbose_level > 0)
+                       printf("Setting up boot volume descriptor\n");
+               cd9660_setup_boot_volume_descriptor(diskStructure, t);
+               sector++;
+       }
+
+       /* Set up the terminator */
+       t = emalloc(sizeof(*t));
+       t->volumeDescriptorData = ecalloc(1, 2048);
+       temp->next = t;
+       t->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_TERMINATOR;
+       t->next = 0;
+       t->volumeDescriptorData[6] = 1;
+       t->sector = sector;
+       memcpy(t->volumeDescriptorData + 1,
+           ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+
+       sector++;
+       return sector;
+}
+
+#if 0
+/*
+ * Populate EAR at some point. Not required, but is used by NetBSD's
+ * cd9660 support
+ */
+static int
+cd9660_fill_extended_attribute_record(cd9660node *node)
+{
+       node->isoExtAttributes = emalloc(sizeof(*node->isoExtAttributes));
+       return 1;
+}
+#endif
+
+static int
+cd9660_translate_node_common(iso9660_disk *diskStructure, cd9660node *newnode)
+{
+       time_t tim;
+       u_char flag;
+       char temp[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+
+       /* Now populate the isoDirRecord structure */
+       memset(temp, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+
+       (void)cd9660_convert_filename(diskStructure, newnode->node->name,
+           temp, !(S_ISDIR(newnode->node->type)));
+
+       flag = ISO_FLAG_CLEAR;
+       if (S_ISDIR(newnode->node->type))
+               flag |= ISO_FLAG_DIRECTORY;
+
+       cd9660_populate_iso_dir_record(newnode->isoDirRecord, 0,
+           flag, strlen(temp), temp);
+
+       /* Set the various dates */
+
+       /* If we want to use the current date and time */
+       time(&tim);
+
+       cd9660_time_915(newnode->isoDirRecord->date, tim);
+
+       cd9660_bothendian_dword(newnode->fileDataLength,
+           newnode->isoDirRecord->size);
+       /* If the file is a link, we want to set the size to 0 */
+       if (S_ISLNK(newnode->node->type))
+               newnode->fileDataLength = 0;
+
+       return 1;
+}
+
+/*
+ * Translate fsnode to cd9660node
+ * Translate filenames and other metadata, including dates, sizes,
+ * permissions, etc
+ * @param struct fsnode * The node generated by makefs
+ * @param struct cd9660node * The intermediate node to be written to
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_translate_node(iso9660_disk *diskStructure, fsnode *node,
+    cd9660node *newnode)
+{
+       if (node == NULL) {
+               if (diskStructure->verbose_level > 0)
+                       printf("cd9660_translate_node: NULL node passed, "
+                              "returning\n");
+               return 0;
+       }
+       newnode->isoDirRecord = emalloc(sizeof(*newnode->isoDirRecord));
+       /* Set the node pointer */
+       newnode->node = node;
+
+       /* Set the size */
+       if (!(S_ISDIR(node->type)))
+               newnode->fileDataLength = node->inode->st.st_size;
+
+       if (cd9660_translate_node_common(diskStructure, newnode) == 0)
+               return 0;
+
+       /* Finally, overwrite some of the values that are set by default */
+       cd9660_time_915(newnode->isoDirRecord->date, node->inode->st.st_mtime);
+
+       return 1;
+}
+
+/*
+ * Compares two ISO filenames
+ * @param const char * The first file name
+ * @param const char * The second file name
+ * @returns : -1 if first is less than second, 0 if they are the same, 1 if
+ *     the second is greater than the first
+ */
+static int
+cd9660_compare_filename(const char *first, const char *second)
+{
+       /*
+        * This can be made more optimal once it has been tested
+        * (the extra character, for example, is for testing)
+        */
+
+       int p1 = 0;
+       int p2 = 0;
+       char c1, c2;
+       /* First, on the filename */
+
+       while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1
+               && p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1) {
+               c1 = first[p1];
+               c2 = second[p2];
+               if (c1 == '.' && c2 =='.')
+                       break;
+               else if (c1 == '.') {
+                       p2++;
+                       c1 = ' ';
+               } else if (c2 == '.') {
+                       p1++;
+                       c2 = ' ';
+               } else {
+                       p1++;
+                       p2++;
+               }
+
+               if (c1 < c2)
+                       return -1;
+               else if (c1 > c2) {
+                       return 1;
+               }
+       }
+
+       if (first[p1] == '.' && second[p2] == '.') {
+               p1++;
+               p2++;
+               while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1
+                       && p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1) {
+                       c1 = first[p1];
+                       c2 = second[p2];
+                       if (c1 == ';' && c2 == ';')
+                               break;
+                       else if (c1 == ';') {
+                               p2++;
+                               c1 = ' ';
+                       } else if (c2 == ';') {
+                               p1++;
+                               c2 = ' ';
+                       } else {
+                               p1++;
+                               p2++;
+                       }
+
+                       if (c1 < c2)
+                               return -1;
+                       else if (c1 > c2)
+                               return 1;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Insert a node into list with ISO sorting rules
+ * @param cd9660node * The head node of the list
+ * @param cd9660node * The node to be inserted
+ */
+static void
+cd9660_sorted_child_insert(cd9660node *parent, cd9660node *cn_new)
+{
+       int compare;
+       cd9660node *cn;
+       struct cd9660_children_head *head = &parent->cn_children;
+
+       /* TODO: Optimize? */
+       cn_new->parent = parent;
+
+       /*
+        * first will either be 0, the . or the ..
+        * if . or .., this means no other entry may be written before first
+        * if 0, the new node may be inserted at the head
+        */
+
+       TAILQ_FOREACH(cn, head, cn_next_child) {
+               /*
+                * Dont insert a node twice -
+                * that would cause an infinite loop
+                */
+               if (cn_new == cn)
+                       return;
+
+               compare = cd9660_compare_filename(cn_new->isoDirRecord->name,
+                       cn->isoDirRecord->name);
+
+               if (compare == 0)
+                       compare = cd9660_compare_filename(cn_new->node->name,
+                               cn->node->name);
+
+               if (compare < 0)
+                       break;
+       }
+       if (cn == NULL)
+               TAILQ_INSERT_TAIL(head, cn_new, cn_next_child);
+       else
+               TAILQ_INSERT_BEFORE(cn, cn_new, cn_next_child);
+}
+
+/*
+ * Called After cd9660_sorted_child_insert
+ * handles file collisions by suffixing each filname with ~n
+ * where n represents the files respective place in the ordering
+ */
+static int
+cd9660_handle_collisions(iso9660_disk *diskStructure, cd9660node *colliding,
+    int past)
+{
+       cd9660node *iter, *next, *prev;
+       int skip;
+       int delete_chars = 0;
+       int temp_past = past;
+       int temp_skip;
+       int flag = 0;
+       cd9660node *end_of_range;
+
+       for (iter = TAILQ_FIRST(&colliding->cn_children);
+            iter != NULL && (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;) {
+               if (strcmp(iter->isoDirRecord->name,
+                          next->isoDirRecord->name) != 0) {
+                       iter = TAILQ_NEXT(iter, cn_next_child);
+                       continue;
+               }
+               flag = 1;
+               temp_skip = skip = cd9660_count_collisions(iter);
+               end_of_range = iter;
+               while (temp_skip > 0) {
+                       temp_skip--;
+                       end_of_range = TAILQ_NEXT(end_of_range, cn_next_child);
+               }
+               temp_past = past;
+               while (temp_past > 0) {
+                       if ((next = TAILQ_NEXT(end_of_range, cn_next_child)) != NULL)
+                               end_of_range = next;
+                       else if ((prev = TAILQ_PREV(iter, cd9660_children_head, cn_next_child)) != NULL)
+                               iter = prev;
+                       else
+                               delete_chars++;
+                       temp_past--;
+               }
+               skip += past;
+               iter = cd9660_rename_filename(diskStructure, iter, skip,
+                   delete_chars);
+       }
+       return flag;
+}
+
+
+static cd9660node *
+cd9660_rename_filename(iso9660_disk *diskStructure, cd9660node *iter, int num,
+    int delete_chars)
+{
+       int i = 0;
+       int numbts, dot, semi, digit, digits, temp, powers, multiplier, count;
+       char *naming;
+       int maxlength;
+        char *tmp;
+
+       if (diskStructure->verbose_level > 0)
+               printf("Rename_filename called\n");
+
+       /* TODO : A LOT of chanes regarding 8.3 filenames */
+       if (diskStructure->isoLevel == 1)
+               maxlength = 8;
+       else if (diskStructure->isoLevel == 2)
+               maxlength = 31;
+       else
+               maxlength = ISO_FILENAME_MAXLENGTH_BEFORE_VERSION;
+
+       tmp = emalloc(ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+
+       while (i < num) {
+               powers = 1;
+               count = 0;
+               digits = 1;
+               multiplier = 1;
+               while (((int)(i / powers) ) >= 10) {
+                       digits++;
+                       powers = powers * 10;
+               }
+
+               naming = iter->o_name;
+
+               /*
+               while ((*naming != '.') && (*naming != ';')) {
+                       naming++;
+                       count++;
+               }
+               */
+
+               dot = -1;
+               semi = -1;
+               while (count < maxlength) {
+                       if (*naming == '.')
+                               dot = count;
+                       else if (*naming == ';') {
+                               semi = count;
+                               break;
+                       }
+                       naming++;
+                       count++;
+               }
+
+               if ((count + digits) < maxlength)
+                       numbts = count;
+               else
+                       numbts = maxlength - (digits);
+               numbts -= delete_chars;
+
+               /* 8.3 rules - keep the extension, add before the dot */
+
+               /*
+                * This code makes a bunch of assumptions.
+                * See if you can spot them all :)
+                */
+
+#if 0
+               if (diskStructure->isoLevel == 1) {
+                       numbts = 8 - digits - delete_chars;
+                       if (dot < 0) {
+
+                       } else {
+                               if (dot < 8) {
+                                       memmove(&tmp[numbts],&tmp[dot],4);
+                               }
+                       }
+               }
+#else
+               __USE(dot);
+               __USE(semi);
+               __USE(multiplier);
+#endif
+
+               /* (copying just the filename before the '.' */
+               memcpy(tmp, (iter->o_name), numbts);
+
+               /* adding the appropriate number following the name */
+               temp = i;
+               while (digits > 0) {
+                       digit = (int)(temp / powers);
+                       temp = temp - digit * powers;
+                       sprintf(&tmp[numbts] , "%d", digit);
+                       digits--;
+                       numbts++;
+                       powers = powers / 10;
+               }
+
+               while ((*naming != ';')  && (numbts < maxlength)) {
+                       tmp[numbts] = (*naming);
+                       naming++;
+                       numbts++;
+               }
+
+               tmp[numbts] = ';';
+               tmp[numbts+1] = '1';
+               tmp[numbts+2] = '\0';
+
+               /*
+                * now tmp has exactly the identifier
+                * we want so we'll copy it back to record
+                */
+               memcpy((iter->isoDirRecord->name), tmp, numbts + 3);
+
+               iter = TAILQ_NEXT(iter, cn_next_child);
+               i++;
+       }
+
+       free(tmp);
+       return iter;
+}
+
+/* Todo: Figure out why these functions are nec. */
+static void
+cd9660_copy_filenames(iso9660_disk *diskStructure, cd9660node *node)
+{
+       cd9660node *cn;
+
+       if (TAILQ_EMPTY(&node->cn_children))
+               return;
+
+       if (TAILQ_FIRST(&node->cn_children)->isoDirRecord == NULL) {
+               debug_print_tree(diskStructure, diskStructure->rootNode, 0);
+               exit(1);
+       }
+
+       TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
+               cd9660_copy_filenames(diskStructure, cn);
+               memcpy(cn->o_name, cn->isoDirRecord->name,
+                   ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+       }
+}
+
+static void
+cd9660_sorting_nodes(cd9660node *node)
+{
+       cd9660node *cn;
+
+       TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
+               cd9660_sorting_nodes(cn);
+       cd9660_sort_nodes(node);
+}
+
+/* XXX Bubble sort. */
+static void
+cd9660_sort_nodes(cd9660node *node)
+{
+       cd9660node *cn, *next;
+
+       do {
+               TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
+                       if ((next = TAILQ_NEXT(cn, cn_next_child)) == NULL)
+                               return;
+                       else if (strcmp(next->isoDirRecord->name,
+                                       cn->isoDirRecord->name) >= 0)
+                               continue;
+                       TAILQ_REMOVE(&node->cn_children, next, cn_next_child);
+                       TAILQ_INSERT_BEFORE(cn, next, cn_next_child);
+                       break;
+               }
+       } while (cn != NULL);
+}
+
+static int
+cd9660_count_collisions(cd9660node *copy)
+{
+       int count = 0;
+       cd9660node *iter, *next;
+
+       for (iter = copy;
+            (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;
+            iter = next) {
+               if (cd9660_compare_filename(iter->isoDirRecord->name,
+                       next->isoDirRecord->name) == 0)
+                       count++;
+               else
+                       return count;
+       }
+#if 0
+       if ((next = TAILQ_NEXT(iter, cn_next_child)) != NULL) {
+               printf("cd9660_recurse_on_collision: count is %i \n", count);
+               compare = cd9660_compare_filename(iter->isoDirRecord->name,
+                       next->isoDirRecord->name);
+               if (compare == 0) {
+                       count++;
+                       return cd9660_recurse_on_collision(next, count);
+               } else
+                       return count;
+       }
+#endif
+       return count;
+}
+
+static cd9660node *
+cd9660_rrip_move_directory(iso9660_disk *diskStructure, cd9660node *dir)
+{
+       char newname[9];
+       cd9660node *tfile;
+
+       /*
+        * This function needs to:
+        * 1) Create an empty virtual file in place of the old directory
+        * 2) Point the virtual file to the new directory
+        * 3) Point the relocated directory to its old parent
+        * 4) Move the directory specified by dir into rr_moved_dir,
+        * and rename it to "diskStructure->rock_ridge_move_count" (as a string)
+        */
+
+       /* First see if the moved directory even exists */
+       if (diskStructure->rr_moved_dir == NULL) {
+               diskStructure->rr_moved_dir = cd9660_create_directory(
+                   diskStructure, ISO_RRIP_DEFAULT_MOVE_DIR_NAME,
+                   diskStructure->rootNode, dir);
+               if (diskStructure->rr_moved_dir == NULL)
+                       return 0;
+       }
+
+       /* Create a file with the same ORIGINAL name */
+       tfile = cd9660_create_file(diskStructure, dir->node->name, dir->parent,
+           dir);
+       if (tfile == NULL)
+               return NULL;
+
+       diskStructure->rock_ridge_move_count++;
+       snprintf(newname, sizeof(newname), "%08i",
+           diskStructure->rock_ridge_move_count);
+
+       /* Point to old parent */
+       dir->rr_real_parent = dir->parent;
+
+       /* Place the placeholder file */
+       if (TAILQ_EMPTY(&dir->rr_real_parent->cn_children)) {
+               TAILQ_INSERT_HEAD(&dir->rr_real_parent->cn_children, tfile,
+                   cn_next_child);
+       } else {
+               cd9660_sorted_child_insert(dir->rr_real_parent, tfile);
+       }
+
+       /* Point to new parent */
+       dir->parent = diskStructure->rr_moved_dir;
+
+       /* Point the file to the moved directory */
+       tfile->rr_relocated = dir;
+
+       /* Actually move the directory */
+       cd9660_sorted_child_insert(diskStructure->rr_moved_dir, dir);
+
+       /* TODO: Inherit permissions / ownership (basically the entire inode) */
+
+       /* Set the new name */
+       memset(dir->isoDirRecord->name, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+       strncpy(dir->isoDirRecord->name, newname, 8);
+       dir->isoDirRecord->length[0] = 34 + 8;
+       dir->isoDirRecord->name_len[0] = 8;
+
+       return dir;
+}
+
+static int
+cd9660_add_dot_records(iso9660_disk *diskStructure, cd9660node *root)
+{
+       struct cd9660_children_head *head = &root->cn_children;
+       cd9660node *cn;
+
+       TAILQ_FOREACH(cn, head, cn_next_child) {
+               if ((cn->type & CD9660_TYPE_DIR) == 0)
+                       continue;
+               /* Recursion first */
+               cd9660_add_dot_records(diskStructure, cn);
+       }
+       cd9660_create_special_directory(diskStructure, CD9660_TYPE_DOT, root);
+       cd9660_create_special_directory(diskStructure, CD9660_TYPE_DOTDOT,
+           root);
+       return 1;
+}
+
+/*
+ * Convert node to cd9660 structure
+ * This function is designed to be called recursively on the root node of
+ * the filesystem
+ * Lots of recursion going on here, want to make sure it is efficient
+ * @param struct fsnode * The root node to be converted
+ * @param struct cd9660* The parent node (should not be NULL)
+ * @param int Current directory depth
+ * @param int* Running count of the number of directories that are being created
+ */
+static void
+cd9660_convert_structure(iso9660_disk *diskStructure, fsnode *root,
+    cd9660node *parent_node, int level, int *numDirectories, int *error)
+{
+       fsnode *iterator = root;
+       cd9660node *this_node;
+       int working_level;
+       int add;
+       int flag = 0;
+       int counter = 0;
+
+       /*
+        * Newer, more efficient method, reduces recursion depth
+        */
+       if (root == NULL) {
+               warnx("%s: root is null\n", __func__);
+               return;
+       }
+
+       /* Test for an empty directory - makefs still gives us the . record */
+       if ((S_ISDIR(root->type)) && (root->name[0] == '.')
+               && (root->name[1] == '\0')) {
+               root = root->next;
+               if (root == NULL)
+                       return;
+       }
+       if ((this_node = cd9660_allocate_cd9660node()) == NULL) {
+               CD9660_MEM_ALLOC_ERROR(__func__);
+       }
+
+       /*
+        * To reduce the number of recursive calls, we will iterate over
+        * the next pointers to the right.
+        */
+       while (iterator != NULL) {
+               add = 1;
+               /*
+                * Increment the directory count if this is a directory
+                * Ignore "." entries. We will generate them later
+                */
+               if (!S_ISDIR(iterator->type) ||
+                   strcmp(iterator->name, ".") != 0) {
+
+                       /* Translate the node, including its filename */
+                       this_node->parent = parent_node;
+                       cd9660_translate_node(diskStructure, iterator,
+                           this_node);
+                       this_node->level = level;
+
+                       if (S_ISDIR(iterator->type)) {
+                               (*numDirectories)++;
+                               this_node->type = CD9660_TYPE_DIR;
+                               working_level = level + 1;
+
+                               /*
+                                * If at level 8, directory would be at 8
+                                * and have children at 9 which is not
+                                * allowed as per ISO spec
+                                */
+                               if (level == 8) {
+                                       if ((!diskStructure->allow_deep_trees) &&
+                                         (!diskStructure->rock_ridge_enabled)) {
+                                               warnx("error: found entry "
+                                                    "with depth greater "
+                                                    "than 8.");
+                                               (*error) = 1;
+                                               return;
+                                       } else if (diskStructure->
+                                                  rock_ridge_enabled) {
+                                               working_level = 3;
+                                               /*
+                                                * Moved directory is actually
+                                                * at level 2.
+                                                */
+                                               this_node->level =
+                                                   working_level - 1;
+                                               if (cd9660_rrip_move_directory(
+                                                       diskStructure,
+                                                       this_node) == 0) {
+                                                       warnx("Failure in "
+                                                             "cd9660_rrip_"
+                                                             "move_directory"
+                                                       );
+                                                       (*error) = 1;
+                                                       return;
+                                               }
+                                               add = 0;
+                                       }
+                               }
+
+                               /* Do the recursive call on the children */
+                               if (iterator->child != 0) {
+                                       cd9660_convert_structure(diskStructure,
+                                               iterator->child, this_node,
+                                               working_level,
+                                               numDirectories, error);
+
+                                       if ((*error) == 1) {
+                                               warnx("%s: Error on recursive "
+                                                   "call", __func__);
+                                               return;
+                                       }
+                               }
+
+                       } else {
+                               /* Only directories should have children */
+                               assert(iterator->child == NULL);
+
+                               this_node->type = CD9660_TYPE_FILE;
+                       }
+
+                       /*
+                        * Finally, do a sorted insert
+                        */
+                       if (add) {
+                               cd9660_sorted_child_insert(
+                                   parent_node, this_node);
+                       }
+
+                       /*Allocate new temp_node */
+                       if (iterator->next != 0) {
+                               this_node = cd9660_allocate_cd9660node();
+                               if (this_node == NULL)
+                                       CD9660_MEM_ALLOC_ERROR(__func__);
+                       }
+               }
+               iterator = iterator->next;
+       }
+
+       /* cd9660_handle_collisions(first_node); */
+
+       /* TODO: need cleanup */
+       cd9660_copy_filenames(diskStructure, parent_node);
+
+       do {
+               flag = cd9660_handle_collisions(diskStructure, parent_node,
+                   counter);
+               counter++;
+               cd9660_sorting_nodes(parent_node);
+       } while ((flag == 1) && (counter < 100));
+}
+
+/*
+ * Clean up the cd9660node tree
+ * This is designed to be called recursively on the root node
+ * @param struct cd9660node *root The node to free
+ * @returns void
+ */
+static void
+cd9660_free_structure(cd9660node *root)
+{
+       cd9660node *cn;
+
+       while ((cn = TAILQ_FIRST(&root->cn_children)) != NULL) {
+               TAILQ_REMOVE(&root->cn_children, cn, cn_next_child);
+               cd9660_free_structure(cn);
+       }
+       free(root);
+}
+
+/*
+ * Be a little more memory conservative:
+ * instead of having the TAILQ_ENTRY as part of the cd9660node,
+ * just create a temporary structure
+ */
+struct ptq_entry
+{
+       TAILQ_ENTRY(ptq_entry) ptq;
+       cd9660node *node;
+} *n;
+
+#define PTQUEUE_NEW(n,s,r,t){\
+       n = emalloc(sizeof(struct s));  \
+       if (n == NULL)  \
+               return r; \
+       n->node = t;\
+}
+
+/*
+ * Generate the path tables
+ * The specific implementation of this function is left as an exercise to the
+ * programmer. It could be done recursively. Make sure you read how the path
+ * table has to be laid out, it has levels.
+ * @param struct iso9660_disk *disk The disk image
+ * @returns int The number of built path tables (between 1 and 4), 0 on failure
+ */
+static int
+cd9660_generate_path_table(iso9660_disk *diskStructure)
+{
+       cd9660node *cn, *dirNode = diskStructure->rootNode;
+       cd9660node *last = dirNode;
+       int pathTableSize = 0;  /* computed as we go */
+       int counter = 1;        /* root gets a count of 0 */
+
+       TAILQ_HEAD(cd9660_pt_head, ptq_entry) pt_head;
+       TAILQ_INIT(&pt_head);
+
+       PTQUEUE_NEW(n, ptq_entry, -1, diskStructure->rootNode);
+
+       /* Push the root node */
+       TAILQ_INSERT_HEAD(&pt_head, n, ptq);
+
+       /* Breadth-first traversal of file structure */
+       while (pt_head.tqh_first != 0) {
+               n = pt_head.tqh_first;
+               dirNode = n->node;
+               TAILQ_REMOVE(&pt_head, pt_head.tqh_first, ptq);
+               free(n);
+
+               /* Update the size */
+               pathTableSize += ISO_PATHTABLE_ENTRY_BASESIZE
+                   + dirNode->isoDirRecord->name_len[0]+
+                       (dirNode->isoDirRecord->name_len[0] % 2 == 0 ? 0 : 1);
+                       /* includes the padding bit */
+
+               dirNode->ptnumber=counter;
+               if (dirNode != last) {
+                       last->ptnext = dirNode;
+                       dirNode->ptprev = last;
+               }
+               last = dirNode;
+
+               /* Push children onto queue */
+               TAILQ_FOREACH(cn, &dirNode->cn_children, cn_next_child) {
+                       /*
+                        * Dont add the DOT and DOTDOT types to the path
+                        * table.
+                        */
+                       if ((cn->type != CD9660_TYPE_DOT)
+                               && (cn->type != CD9660_TYPE_DOTDOT)) {
+
+                               if (S_ISDIR(cn->node->type)) {
+                                       PTQUEUE_NEW(n, ptq_entry, -1, cn);
+                                       TAILQ_INSERT_TAIL(&pt_head, n, ptq);
+                               }
+                       }
+               }
+               counter++;
+       }
+       return pathTableSize;
+}
+
+void
+cd9660_compute_full_filename(cd9660node *node, char *buf)
+{
+       int len;
+
+       len = CD9660MAXPATH + 1;
+       len = snprintf(buf, len, "%s/%s/%s", node->node->root,
+           node->node->path, node->node->name);
+       if (len > CD9660MAXPATH)
+               errx(1, "Pathname too long.");
+}
+
+/* NEW filename conversion method */
+typedef int(*cd9660_filename_conversion_functor)(iso9660_disk *, const char *,
+    char *, int);
+
+
+/*
+ * TODO: These two functions are almost identical.
+ * Some code cleanup is possible here
+ *
+ * XXX bounds checking!
+ */
+static int
+cd9660_level1_convert_filename(iso9660_disk *diskStructure, const char *oldname,
+    char *newname, int is_file)
+{
+       /*
+        * ISO 9660 : 10.1
+        * File Name shall not contain more than 8 d or d1 characters
+        * File Name Extension shall not contain more than 3 d or d1 characters
+        * Directory Identifier shall not contain more than 8 d or d1 characters
+        */
+       int namelen = 0;
+       int extlen = 0;
+       int found_ext = 0;
+
+       while (*oldname != '\0' && extlen < 3) {
+               /* Handle period first, as it is special */
+               if (*oldname == '.') {
+                       if (found_ext) {
+                               *newname++ = '_';
+                               extlen ++;
+                       }
+                       else {
+                               *newname++ = '.';
+                               found_ext = 1;
+                       }
+               } else {
+                       /* cut RISC OS file type off ISO name */
+                       if (diskStructure->archimedes_enabled &&
+                           *oldname == ',' && strlen(oldname) == 4)
+                               break;
+
+                       /* Enforce 12.3 / 8 */
+                       if (namelen == 8 && !found_ext)
+                               break;
+
+                       if (islower((unsigned char)*oldname))
+                               *newname++ = toupper((unsigned char)*oldname);
+                       else if (isupper((unsigned char)*oldname)
+                           || isdigit((unsigned char)*oldname))
+                               *newname++ = *oldname;
+                       else
+                               *newname++ = '_';
+
+                       if (found_ext)
+                               extlen++;
+                       else
+                               namelen++;
+               }
+               oldname++;
+       }
+       if (is_file) {
+               if (!found_ext && !diskStructure->omit_trailing_period)
+                       *newname++ = '.';
+               /* Add version */
+               sprintf(newname, ";%i", 1);
+       }
+       return namelen + extlen + found_ext;
+}
+
+/* XXX bounds checking! */
+static int
+cd9660_level2_convert_filename(iso9660_disk *diskStructure, const char *oldname,
+    char *newname, int is_file)
+{
+       /*
+        * ISO 9660 : 7.5.1
+        * File name : 0+ d or d1 characters
+        * separator 1 (.)
+        * File name extension : 0+ d or d1 characters
+        * separator 2 (;)
+        * File version number (5 characters, 1-32767)
+        * 1 <= Sum of File name and File name extension <= 30
+        */
+       int namelen = 0;
+       int extlen = 0;
+       int found_ext = 0;
+
+       while (*oldname != '\0' && namelen + extlen < 30) {
+               /* Handle period first, as it is special */
+               if (*oldname == '.') {
+                       if (found_ext) {
+                               if (diskStructure->allow_multidot) {
+                                       *newname++ = '.';
+                               } else {
+                                       *newname++ = '_';
+                               }
+                               extlen ++;
+                       }
+                       else {
+                               *newname++ = '.';
+                               found_ext = 1;
+                       }
+               } else {
+                       /* cut RISC OS file type off ISO name */
+                       if (diskStructure->archimedes_enabled &&
+                           *oldname == ',' && strlen(oldname) == 4)
+                               break;
+
+                        if (islower((unsigned char)*oldname))
+                               *newname++ = toupper((unsigned char)*oldname);
+                       else if (isupper((unsigned char)*oldname) ||
+                           isdigit((unsigned char)*oldname))
+                               *newname++ = *oldname;
+                       else if (diskStructure->allow_multidot &&
+                           *oldname == '.') {
+                               *newname++ = '.';
+                       } else {
+                               *newname++ = '_';
+                       }
+
+                       if (found_ext)
+                               extlen++;
+                       else
+                               namelen++;
+               }
+               oldname ++;
+       }
+       if (is_file) {
+               if (!found_ext && !diskStructure->omit_trailing_period)
+                       *newname++ = '.';
+               /* Add version */
+               sprintf(newname, ";%i", 1);
+       }
+       return namelen + extlen + found_ext;
+}
+
+#if 0
+static int
+cd9660_joliet_convert_filename(iso9660_disk *diskStructure, const char *oldname,
+    char *newname, int is_file)
+{
+       /* TODO: implement later, move to cd9660_joliet.c ?? */
+}
+#endif
+
+
+/*
+ * Convert a file name to ISO compliant file name
+ * @param char * oldname The original filename
+ * @param char ** newname The new file name, in the appropriate character
+ *                        set and of appropriate length
+ * @param int 1 if file, 0 if directory
+ * @returns int The length of the new string
+ */
+static int
+cd9660_convert_filename(iso9660_disk *diskStructure, const char *oldname,
+    char *newname, int is_file)
+{
+       /* NEW */
+       cd9660_filename_conversion_functor conversion_function = 0;
+       if (diskStructure->isoLevel == 1)
+               conversion_function = &cd9660_level1_convert_filename;
+       else if (diskStructure->isoLevel == 2)
+               conversion_function = &cd9660_level2_convert_filename;
+       return (*conversion_function)(diskStructure, oldname, newname, is_file);
+}
+
+int
+cd9660_compute_record_size(iso9660_disk *diskStructure, cd9660node *node)
+{
+       int size = node->isoDirRecord->length[0];
+
+       if (diskStructure->rock_ridge_enabled)
+               size += node->susp_entry_size;
+       size += node->su_tail_size;
+       size += size & 1; /* Ensure length of record is even. */
+       assert(size <= 254);
+       return size;
+}
+
+static void
+cd9660_populate_dot_records(iso9660_disk *diskStructure, cd9660node *node)
+{
+       node->dot_record->fileDataSector = node->fileDataSector;
+       memcpy(node->dot_record->isoDirRecord,node->isoDirRecord, 34);
+       node->dot_record->isoDirRecord->name_len[0] = 1;
+       node->dot_record->isoDirRecord->name[0] = 0;
+       node->dot_record->isoDirRecord->name[1] = 0;
+       node->dot_record->isoDirRecord->length[0] = 34;
+       node->dot_record->fileRecordSize =
+           cd9660_compute_record_size(diskStructure, node->dot_record);
+
+       if (node == diskStructure->rootNode) {
+               node->dot_dot_record->fileDataSector = node->fileDataSector;
+               memcpy(node->dot_dot_record->isoDirRecord,node->isoDirRecord,
+                   34);
+       } else {
+               node->dot_dot_record->fileDataSector =
+                   node->parent->fileDataSector;
+               memcpy(node->dot_dot_record->isoDirRecord,
+                   node->parent->isoDirRecord,34);
+       }
+       node->dot_dot_record->isoDirRecord->name_len[0] = 1;
+       node->dot_dot_record->isoDirRecord->name[0] = 1;
+       node->dot_dot_record->isoDirRecord->name[1] = 0;
+       node->dot_dot_record->isoDirRecord->length[0] = 34;
+       node->dot_dot_record->fileRecordSize =
+           cd9660_compute_record_size(diskStructure, node->dot_dot_record);
+}
+
+/*
+ * @param struct cd9660node *node The node
+ * @param int The offset (in bytes) - SHOULD align to the beginning of a sector
+ * @returns int The total size of files and directory entries (should be
+ *              a multiple of sector size)
+*/
+static int64_t
+cd9660_compute_offsets(iso9660_disk *diskStructure, cd9660node *node,
+    int64_t startOffset)
+{
+       /*
+        * This function needs to compute the size of directory records and
+        * runs, file lengths, and set the appropriate variables both in
+        * cd9660node and isoDirEntry
+        */
+       int64_t used_bytes = 0;
+       int64_t current_sector_usage = 0;
+       cd9660node *child;
+       fsinode *inode;
+       int64_t r;
+
+       assert(node != NULL);
+
+
+       /*
+        * NOTE : There needs to be some special case detection for
+        * the "real root" node, since for it, node->node is undefined
+        */
+
+       node->fileDataSector = -1;
+
+       if (node->type & CD9660_TYPE_DIR) {
+               node->fileRecordSize = cd9660_compute_record_size(
+                   diskStructure, node);
+               /*Set what sector this directory starts in*/
+               node->fileDataSector =
+                   CD9660_BLOCKS(diskStructure->sectorSize,startOffset);
+
+               cd9660_bothendian_dword(node->fileDataSector,
+                   node->isoDirRecord->extent);
+
+               /*
+                * First loop over children, need to know the size of
+                * their directory records
+                */
+               node->fileSectorsUsed = 1;
+               TAILQ_FOREACH(child, &node->cn_children, cn_next_child) {
+                       node->fileDataLength +=
+                           cd9660_compute_record_size(diskStructure, child);
+                       if ((cd9660_compute_record_size(diskStructure, child) +
+                           current_sector_usage) >=
+                           diskStructure->sectorSize) {
+                               current_sector_usage = 0;
+                               node->fileSectorsUsed++;
+                       }
+
+                       current_sector_usage +=
+                           cd9660_compute_record_size(diskStructure, child);
+               }
+
+               cd9660_bothendian_dword(node->fileSectorsUsed *
+                       diskStructure->sectorSize,node->isoDirRecord->size);
+
+               /*
+                * This should point to the sector after the directory
+                * record (or, the first byte in that sector)
+                */
+               used_bytes += node->fileSectorsUsed * diskStructure->sectorSize;
+
+               for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child);
+                    child != NULL; child = TAILQ_NEXT(child, cn_next_child)) {
+                       /* Directories need recursive call */
+                       if (S_ISDIR(child->node->type)) {
+                               r = cd9660_compute_offsets(diskStructure, child,
+                                   used_bytes + startOffset);
+
+                               if (r != -1)
+                                       used_bytes += r;
+                               else
+                                       return -1;
+                       }
+               }
+
+               /* Explicitly set the . and .. records */
+               cd9660_populate_dot_records(diskStructure, node);
+
+               /* Finally, do another iteration to write the file data*/
+               for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child);
+                    child != NULL;
+                    child = TAILQ_NEXT(child, cn_next_child)) {
+                       /* Files need extent set */
+                       if (S_ISDIR(child->node->type))
+                               continue;
+                       child->fileRecordSize =
+                           cd9660_compute_record_size(diskStructure, child);
+
+                       child->fileSectorsUsed =
+                           CD9660_BLOCKS(diskStructure->sectorSize,
+                               child->fileDataLength);
+
+                       inode = child->node->inode;
+                       if ((inode->flags & FI_ALLOCATED) == 0) {
+                               inode->ino =
+                                   CD9660_BLOCKS(diskStructure->sectorSize,
+                                       used_bytes + startOffset);
+                               inode->flags |= FI_ALLOCATED;
+                               used_bytes += child->fileSectorsUsed *
+                                   diskStructure->sectorSize;
+                       } else {
+                               INODE_WARNX(("%s: already allocated inode %d "
+                                     "data sectors at %" PRIu32, __func__,
+                                     (int)inode->st.st_ino, inode->ino));
+                       }
+                       child->fileDataSector = inode->ino;
+                       cd9660_bothendian_dword(child->fileDataSector,
+                               child->isoDirRecord->extent);
+               }
+       }
+
+       return used_bytes;
+}
+
+#if 0
+/* Might get rid of this func */
+static int
+cd9660_copy_stat_info(cd9660node *from, cd9660node *to, int file)
+{
+       to->node->inode->st.st_dev = 0;
+       to->node->inode->st.st_ino = 0;
+       to->node->inode->st.st_size = 0;
+       to->node->inode->st.st_blksize = from->node->inode->st.st_blksize;
+       to->node->inode->st.st_atime = from->node->inode->st.st_atime;
+       to->node->inode->st.st_mtime = from->node->inode->st.st_mtime;
+       to->node->inode->st.st_ctime = from->node->inode->st.st_ctime;
+       to->node->inode->st.st_uid = from->node->inode->st.st_uid;
+       to->node->inode->st.st_gid = from->node->inode->st.st_gid;
+       to->node->inode->st.st_mode = from->node->inode->st.st_mode;
+       /* Clear out type */
+       to->node->inode->st.st_mode = to->node->inode->st.st_mode & ~(S_IFMT);
+       if (file)
+               to->node->inode->st.st_mode |= S_IFREG;
+       else
+               to->node->inode->st.st_mode |= S_IFDIR;
+       return 1;
+}
+#endif
+
+static cd9660node *
+cd9660_create_virtual_entry(iso9660_disk *diskStructure, const char *name,
+    cd9660node *parent, int file, int insert)
+{
+       cd9660node *temp;
+       fsnode * tfsnode;
+
+       assert(parent != NULL);
+
+       temp = cd9660_allocate_cd9660node();
+       if (temp == NULL)
+               return NULL;
+
+       tfsnode = emalloc(sizeof(*tfsnode));
+       tfsnode->name = estrdup(name);
+       temp->isoDirRecord = emalloc(sizeof(*temp->isoDirRecord));
+
+       cd9660_convert_filename(diskStructure, tfsnode->name,
+           temp->isoDirRecord->name, file);
+
+       temp->node = tfsnode;
+       temp->parent = parent;
+
+       if (insert) {
+               if (temp->parent != NULL) {
+                       temp->level = temp->parent->level + 1;
+                       if (!TAILQ_EMPTY(&temp->parent->cn_children))
+                               cd9660_sorted_child_insert(temp->parent, temp);
+                       else
+                               TAILQ_INSERT_HEAD(&temp->parent->cn_children,
+                                   temp, cn_next_child);
+               }
+       }
+
+       if (parent->node != NULL) {
+               tfsnode->type = parent->node->type;
+       }
+
+       /* Clear out file type bits */
+       tfsnode->type &= ~(S_IFMT);
+       if (file)
+               tfsnode->type |= S_IFREG;
+       else
+               tfsnode->type |= S_IFDIR;
+
+       /* Indicate that there is no spec entry (inode) */
+       tfsnode->flags &= ~(FSNODE_F_HASSPEC);
+#if 0
+       cd9660_copy_stat_info(parent, temp, file);
+#endif
+       return temp;
+}
+
+static cd9660node *
+cd9660_create_file(iso9660_disk *diskStructure, const char *name,
+    cd9660node *parent, cd9660node *me)
+{
+       cd9660node *temp;
+
+       temp = cd9660_create_virtual_entry(diskStructure, name, parent, 1, 1);
+       if (temp == NULL)
+               return NULL;
+
+       temp->fileDataLength = 0;
+
+       temp->type = CD9660_TYPE_FILE | CD9660_TYPE_VIRTUAL;
+
+       temp->node->inode = ecalloc(1, sizeof(*temp->node->inode));
+       *temp->node->inode = *me->node->inode;
+
+       if (cd9660_translate_node_common(diskStructure, temp) == 0)
+               return NULL;
+       return temp;
+}
+
+/*
+ * Create a new directory which does not exist on disk
+ * @param const char * name The name to assign to the directory
+ * @param const char * parent Pointer to the parent directory
+ * @returns cd9660node * Pointer to the new directory
+ */
+static cd9660node *
+cd9660_create_directory(iso9660_disk *diskStructure, const char *name,
+    cd9660node *parent, cd9660node *me)
+{
+       cd9660node *temp;
+
+       temp = cd9660_create_virtual_entry(diskStructure, name, parent, 0, 1);
+       if (temp == NULL)
+               return NULL;
+       temp->node->type |= S_IFDIR;
+
+       temp->type = CD9660_TYPE_DIR | CD9660_TYPE_VIRTUAL;
+
+       temp->node->inode = ecalloc(1, sizeof(*temp->node->inode));
+       *temp->node->inode = *me->node->inode;
+
+       if (cd9660_translate_node_common(diskStructure, temp) == 0)
+               return NULL;
+       return temp;
+}
+
+static cd9660node *
+cd9660_create_special_directory(iso9660_disk *diskStructure, u_char type,
+    cd9660node *parent)
+{
+       cd9660node *temp, *first;
+       char na[2];
+
+       assert(parent != NULL);
+
+       if (type == CD9660_TYPE_DOT)
+               na[0] = 0;
+       else if (type == CD9660_TYPE_DOTDOT)
+               na[0] = 1;
+       else
+               return 0;
+
+       na[1] = 0;
+       if ((temp = cd9660_create_virtual_entry(diskStructure, na, parent,
+           0, 0)) == NULL)
+               return NULL;
+
+       temp->parent = parent;
+       temp->type = type;
+       temp->isoDirRecord->length[0] = 34;
+       /* Dot record is always first */
+       if (type == CD9660_TYPE_DOT) {
+               parent->dot_record = temp;
+               TAILQ_INSERT_HEAD(&parent->cn_children, temp, cn_next_child);
+       /* DotDot should be second */
+       } else if (type == CD9660_TYPE_DOTDOT) {
+               parent->dot_dot_record = temp;
+               /*
+                 * If the first child is the dot record, insert
+                 * this second.  Otherwise, insert it at the head.
+                */
+               if ((first = TAILQ_FIRST(&parent->cn_children)) == NULL ||
+                   (first->type & CD9660_TYPE_DOT) == 0) {
+                       TAILQ_INSERT_HEAD(&parent->cn_children, temp,
+                           cn_next_child);
+               } else {
+                       TAILQ_INSERT_AFTER(&parent->cn_children, first, temp,
+                           cn_next_child);
+               }
+       }
+
+       return temp;
+}
+
+static int
+cd9660_add_generic_bootimage(iso9660_disk *diskStructure, const char *bootimage)
+{
+       struct stat stbuf;
+
+       assert(bootimage != NULL);
+
+       if (*bootimage == '\0') {
+               warnx("Error: Boot image must be a filename");
+               return 0;
+       }
+
+       diskStructure->generic_bootimage = estrdup(bootimage);
+
+       /* Get information about the file */
+       if (lstat(diskStructure->generic_bootimage, &stbuf) == -1)
+               err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
+                   diskStructure->generic_bootimage);
+
+       if (stbuf.st_size > 32768) {
+               warnx("Error: Boot image must be no greater than 32768 bytes");
+               return 0;
+       }
+
+       if (diskStructure->verbose_level > 0) {
+               printf("Generic boot image image has size %lld\n",
+                   (long long)stbuf.st_size);
+       }
+
+       diskStructure->has_generic_bootimage = 1;
+
+       return 1;
+}
diff --git a/usr.sbin/makefs/cd9660.h b/usr.sbin/makefs/cd9660.h
new file mode 100644 (file)
index 0000000..ef9f44f
--- /dev/null
@@ -0,0 +1,357 @@
+/*     $NetBSD: cd9660.h,v 1.20 2013/01/29 15:52:25 christos Exp $     */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+
+#ifndef _MAKEFS_CD9660_H
+#define _MAKEFS_CD9660_H
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+
+#include "makefs.h"
+#include "iso.h"
+#include "iso_rrip.h"
+#include "cd9660/cd9660_eltorito.h"
+
+#ifdef DEBUG
+#define        INODE_WARNX(__x)        warnx __x
+#else /* DEBUG */
+#define        INODE_WARNX(__x)
+#endif /* DEBUG */
+
+#define CD9660MAXPATH 4096
+
+#define ISO_STRING_FILTER_NONE = 0x00
+#define ISO_STRING_FILTER_DCHARS = 0x01
+#define ISO_STRING_FILTER_ACHARS = 0x02
+
+/*
+Extended preferences type, in the spirit of what makefs gives us (only ints)
+*/
+typedef struct {
+       const char  *shortName;         /* Short option */
+       const char      *name;          /* option name */
+       char            *value;         /* where to stuff the value */
+       int             minLength;      /* minimum for value */
+       int             maxLength;      /* maximum for value */
+       const char      *desc;          /* option description */
+       int             filterFlags;
+} string_option_t;
+
+/******** STRUCTURES **********/
+
+/*Defaults*/
+#define ISO_DEFAULT_VOLUMEID "MAKEFS_CD9660_IMAGE"
+#define ISO_DEFAULT_APPID "MAKEFS"
+#define ISO_DEFAULT_PUBLISHER "MAKEFS"
+#define ISO_DEFAULT_PREPARER "MAKEFS"
+
+#define ISO_VOLUME_DESCRIPTOR_STANDARD_ID "CD001"
+#define ISO_VOLUME_DESCRIPTOR_BOOT 0
+#define ISO_VOLUME_DESCRIPTOR_PVD 1
+#define ISO_VOLUME_DESCRIPTOR_TERMINATOR 255
+
+/*30 for name and extension, as well as version number and padding bit*/
+#define ISO_FILENAME_MAXLENGTH_BEFORE_VERSION 30
+#define ISO_FILENAME_MAXLENGTH 36
+#define ISO_FILENAME_MAXLENGTH_WITH_PADDING 37
+
+#define ISO_FLAG_CLEAR 0x00
+#define ISO_FLAG_HIDDEN 0x01
+#define ISO_FLAG_DIRECTORY 0x02
+#define ISO_FLAG_ASSOCIATED 0x04
+#define ISO_FLAG_PERMISSIONS 0x08
+#define ISO_FLAG_RESERVED5 0x10
+#define ISO_FLAG_RESERVED6 0x20
+#define ISO_FLAG_FINAL_RECORD 0x40
+
+#define ISO_PATHTABLE_ENTRY_BASESIZE 8
+
+#define ISO_RRIP_DEFAULT_MOVE_DIR_NAME "RR_MOVED"
+#define RRIP_DEFAULT_MOVE_DIR_NAME ".rr_moved"
+
+#define        CD9660_BLOCKS(__sector_size, __bytes)   \
+       howmany((__bytes), (__sector_size))
+
+#define CD9660_MEM_ALLOC_ERROR(_F)     \
+    err(EXIT_FAILURE, "%s, %s l. %d", _F, __FILE__, __LINE__)
+
+#define CD9660_TYPE_FILE       0x01
+#define CD9660_TYPE_DIR                0x02
+#define CD9660_TYPE_DOT                0x04
+#define CD9660_TYPE_DOTDOT     0x08
+#define CD9660_TYPE_VIRTUAL    0x80
+
+#define CD9660_INODE_HASH_SIZE 1024
+#define CD9660_SECTOR_SIZE     2048
+
+#define CD9660_END_PADDING     150
+
+/* Slight modification of the ISO structure in iso.h */
+typedef struct _iso_directory_record_cd9660 {
+       u_char length                   [ISODCL (1, 1)];        /* 711 */
+       u_char ext_attr_length          [ISODCL (2, 2)];        /* 711 */
+       u_char extent                   [ISODCL (3, 10)];       /* 733 */
+       u_char size                     [ISODCL (11, 18)];      /* 733 */
+       u_char date                     [ISODCL (19, 25)];      /* 7 by 711 */
+       u_char flags                    [ISODCL (26, 26)];
+       u_char file_unit_size           [ISODCL (27, 27)];      /* 711 */
+       u_char interleave               [ISODCL (28, 28)];      /* 711 */
+       u_char volume_sequence_number   [ISODCL (29, 32)];      /* 723 */
+       u_char name_len                 [ISODCL (33, 33)];      /* 711 */
+       char name                       [ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+} iso_directory_record_cd9660;
+
+/* TODO: Lots of optimization of this structure */
+typedef struct _cd9660node {
+       u_char  type;/* Used internally */
+       /* Tree structure */
+       struct _cd9660node      *parent;        /* parent (NULL if root) */
+       TAILQ_HEAD(cd9660_children_head, _cd9660node)   cn_children;
+       TAILQ_ENTRY(_cd9660node)                cn_next_child;
+
+       struct _cd9660node *dot_record; /* For directories, used mainly in RRIP */
+       struct _cd9660node *dot_dot_record;
+
+       fsnode          *node;          /* pointer to fsnode */
+       struct _iso_directory_record_cd9660     *isoDirRecord;
+       struct iso_extended_attributes  *isoExtAttributes;
+
+       /***** SIZE CALCULATION *****/
+       /*already stored in isoDirRecord, but this is an int version, and will be
+               copied to isoDirRecord on writing*/
+       uint32_t fileDataSector;
+
+       /*
+        * same thing, though some notes:
+        * If a file, this is the file size
+        * If a directory, this is the size of all its children's
+        *      directory records
+        * plus necessary padding
+        */
+       int64_t fileDataLength;
+
+       int64_t fileSectorsUsed;
+       int fileRecordSize;/*copy of a variable, int for quicker calculations*/
+
+       /* Old name, used for renaming - needs to be optimized but low priority */
+       char o_name [ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+
+       /***** SPACE RESERVED FOR EXTENSIONS *****/
+       /* For memory efficiency's sake - we should move this to a separate struct
+               and point to null if not needed */
+       /* For Rock Ridge */
+       struct _cd9660node *rr_real_parent, *rr_relocated;
+
+       int64_t susp_entry_size;
+       int64_t susp_dot_entry_size;
+       int64_t susp_dot_dot_entry_size;
+
+       /* Continuation area stuff */
+       int64_t susp_entry_ce_start;
+       int64_t susp_dot_ce_start;
+       int64_t susp_dot_dot_ce_start;
+
+       int64_t susp_entry_ce_length;
+       int64_t susp_dot_ce_length;
+       int64_t susp_dot_dot_ce_length;
+
+       /* Data to put at the end of the System Use field */
+       int64_t su_tail_size;
+       char *su_tail_data;
+
+       /*** PATH TABLE STUFF ***/
+       int level;                      /*depth*/
+       int ptnumber;
+       struct _cd9660node *ptnext, *ptprev, *ptlast;
+
+       /* SUSP entries */
+       TAILQ_HEAD(susp_linked_list, ISO_SUSP_ATTRIBUTES) head;
+} cd9660node;
+
+typedef struct _path_table_entry
+{
+       u_char length[ISODCL (1, 1)];
+       u_char extended_attribute_length[ISODCL (2, 2)];
+       u_char first_sector[ISODCL (3, 6)];
+       u_char parent_number[ISODCL (7, 8)];
+       u_char name[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+} path_table_entry;
+
+typedef struct _volume_descriptor
+{
+       u_char *volumeDescriptorData; /*ALWAYS 2048 bytes long*/
+       int64_t sector;
+       struct _volume_descriptor *next;
+} volume_descriptor;
+
+typedef struct _iso9660_disk {
+       int sectorSize;
+       struct iso_primary_descriptor           primaryDescriptor;
+       struct iso_supplementary_descriptor     supplementaryDescriptor;
+
+       volume_descriptor *firstVolumeDescriptor;
+
+       cd9660node *rootNode;
+
+       /* Important sector numbers here */
+       /* primaryDescriptor.type_l_path_table*/
+       int64_t primaryBigEndianTableSector;
+
+       /* primaryDescriptor.type_m_path_table*/
+       int64_t primaryLittleEndianTableSector;
+
+       /* primaryDescriptor.opt_type_l_path_table*/
+       int64_t secondaryBigEndianTableSector;
+
+       /* primaryDescriptor.opt_type_m_path_table*/
+       int64_t secondaryLittleEndianTableSector;
+
+       /* primaryDescriptor.path_table_size*/
+       int pathTableLength;
+       int64_t dataFirstSector;
+
+       int64_t totalSectors;
+       /* OPTIONS GO HERE */
+       int     isoLevel;
+
+       int include_padding_areas;
+
+       int follow_sym_links;
+       int verbose_level;
+       int displayHelp;
+       int keep_bad_images;
+
+       /* SUSP options and variables */
+       int64_t susp_continuation_area_start_sector;
+       int64_t susp_continuation_area_size;
+       int64_t susp_continuation_area_current_free;
+
+       int rock_ridge_enabled;
+       /* Other Rock Ridge Variables */
+       char *rock_ridge_renamed_dir_name;
+       int rock_ridge_move_count;
+       cd9660node *rr_moved_dir;
+
+       int archimedes_enabled;
+       int chrp_boot;
+
+       /* Spec breaking options */
+       u_char allow_deep_trees;
+       u_char allow_start_dot;
+       u_char allow_max_name; /* Allow 37 char filenames*/
+       u_char allow_illegal_chars; /* ~, !, # */
+       u_char allow_lowercase;
+       u_char allow_multidot;
+       u_char omit_trailing_period;
+
+       /* BOOT INFORMATION HERE */
+       int has_generic_bootimage; /* Default to 0 */
+       char *generic_bootimage;
+
+       int is_bootable;/* Default to 0 */
+       int64_t boot_catalog_sector;
+       boot_volume_descriptor *boot_descriptor;
+       char * boot_image_directory;
+
+       TAILQ_HEAD(boot_image_list,cd9660_boot_image) boot_images;
+       int image_serialno;
+       LIST_HEAD(boot_catalog_entries,boot_catalog_entry) boot_entries;
+
+} iso9660_disk;
+
+/************ FUNCTIONS **************/
+int                    cd9660_valid_a_chars(const char *);
+int                    cd9660_valid_d_chars(const char *);
+void                   cd9660_uppercase_characters(char *, int);
+
+/* ISO Data Types */
+void                   cd9660_721(uint16_t, unsigned char *);
+void                   cd9660_731(uint32_t, unsigned char *);
+void                   cd9660_722(uint16_t, unsigned char *);
+void                   cd9660_732(uint32_t, unsigned char *);
+void                   cd9660_bothendian_dword(uint32_t dw, unsigned char *);
+void                   cd9660_bothendian_word(uint16_t dw, unsigned char *);
+void                   cd9660_set_date(char *, time_t);
+void                   cd9660_time_8426(unsigned char *, time_t);
+void                   cd9660_time_915(unsigned char *, time_t);
+
+/*** Boot Functions ***/
+int    cd9660_write_generic_bootimage(FILE *);
+int    cd9660_write_boot(iso9660_disk *, FILE *);
+int    cd9660_add_boot_disk(iso9660_disk *, const char *);
+int    cd9660_eltorito_add_boot_option(iso9660_disk *, const char *,
+    const char *);
+int    cd9660_setup_boot(iso9660_disk *, int);
+int    cd9660_setup_boot_volume_descriptor(iso9660_disk *,
+    volume_descriptor *);
+
+
+/*** Write Functions ***/
+int    cd9660_write_image(iso9660_disk *, const char *image);
+int    cd9660_copy_file(iso9660_disk *, FILE *, off_t, const char *);
+
+void   cd9660_compute_full_filename(cd9660node *, char *);
+int    cd9660_compute_record_size(iso9660_disk *, cd9660node *);
+
+/* Debugging functions */
+void   debug_print_tree(iso9660_disk *, cd9660node *,int);
+void   debug_print_path_tree(cd9660node *);
+void   debug_print_volume_descriptor_information(iso9660_disk *);
+void   debug_dump_to_xml_ptentry(path_table_entry *,int, int);
+void   debug_dump_to_xml_path_table(FILE *, off_t, int, int);
+void   debug_dump_to_xml(FILE *);
+int    debug_get_encoded_number(unsigned char *, int);
+void   debug_dump_integer(const char *, char *,int);
+void   debug_dump_string(const char *,unsigned char *,int);
+void   debug_dump_directory_record_9_1(unsigned char *);
+void   debug_dump_to_xml_volume_descriptor(unsigned char *,int);
+
+void   cd9660_pad_string_spaces(char *, int);
+
+#endif
diff --git a/usr.sbin/makefs/cd9660/Makefile.inc b/usr.sbin/makefs/cd9660/Makefile.inc
new file mode 100644 (file)
index 0000000..bffba66
--- /dev/null
@@ -0,0 +1,15 @@
+#      $NetBSD: Makefile.inc,v 1.3 2012/08/10 12:10:29 joerg Exp $
+#
+
+.PATH: ${.CURDIR}/cd9660 ${NETBSDSRCDIR}/sys/fs/cd9660
+
+CPPFLAGS+=-I${NETBSDSRCDIR}/sys/fs/cd9660
+
+SRCS+= cd9660_strings.c cd9660_debug.c cd9660_eltorito.c
+SRCS+= cd9660_write.c cd9660_conversion.c iso9660_rrip.c cd9660_archimedes.c
+
+.if !defined(HOSTPROGNAME)
+.for f in cd9660_debug cd9660_write
+COPTS.${f}.c+=  -Wno-pointer-sign
+.endfor
+.endif
diff --git a/usr.sbin/makefs/cd9660/cd9660_archimedes.c b/usr.sbin/makefs/cd9660/cd9660_archimedes.c
new file mode 100644 (file)
index 0000000..45bf2ac
--- /dev/null
@@ -0,0 +1,130 @@
+/* $NetBSD: cd9660_archimedes.c,v 1.2 2013/01/28 21:03:28 christos Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2009 Ben Harris
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+/*
+ * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension
+ *
+ * RISC OS CDFS looks for a special block at the end of the System Use
+ * Field for each file.  If present, this contains the RISC OS load
+ * and exec address (used to hold the file timestamp and type), the
+ * file attributes, and a flag indicating whether the first character
+ * of the filename should be replaced with '!' (since many special
+ * RISC OS filenames do).
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: cd9660_archimedes.c,v 1.2 2013/01/28 21:03:28 christos Exp $");
+#endif  /* !__lint */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "cd9660_archimedes.h"
+
+/*
+ * Convert a Unix time_t (non-leap seconds since 1970-01-01) to a RISC
+ * OS time (non-leap(?) centiseconds since 1900-01-01(?)).
+ */
+
+static u_int64_t
+riscos_date(time_t unixtime)
+{
+       u_int64_t base;
+
+       base = 31536000ULL * 70 + 86400 * 17;
+       return (((u_int64_t)unixtime) + base)*100;
+}
+
+/*
+ * Add "ARCHIMEDES" metadata to a node if that seems appropriate.
+ *
+ * We touch regular files with names matching /,[0-9a-f]{3}$/ and
+ * directories matching /^!/.
+ */
+static void
+archimedes_convert_node(cd9660node *node)
+{
+       struct ISO_ARCHIMEDES *arc;
+       size_t len;
+       int type = -1;
+       uint64_t stamp;
+
+       if (node->su_tail_data != NULL)
+               /* Something else already has the tail. */
+               return;
+
+       len = strlen(node->node->name);
+       if (len < 1) return;
+
+       if (len >= 4 && node->node->name[len-4] == ',')
+               /* XXX should support ,xxx and ,lxa */
+               type = strtoul(node->node->name + len - 3, NULL, 16);
+       if (type == -1 && node->node->name[0] != '!')
+               return;
+       if (type == -1) type = 0;
+
+       assert(sizeof(*arc) == 32);
+       arc = ecalloc(1, sizeof(*arc));
+
+       stamp = riscos_date(node->node->inode->st.st_mtime);
+
+       memcpy(arc->magic, "ARCHIMEDES", 10);
+       cd9660_731(0xfff00000 | (type << 8) | (stamp >> 32), arc->loadaddr);
+       cd9660_731(stamp & 0x00ffffffffULL, arc->execaddr);
+       arc->ro_attr = RO_ACCESS_UR | RO_ACCESS_OR;
+       arc->cdfs_attr = node->node->name[0] == '!' ? CDFS_PLING : 0;
+       node->su_tail_data = (void *)arc;
+       node->su_tail_size = sizeof(*arc);
+}
+
+/*
+ * Add "ARCHIMEDES" metadata to an entire tree recursively.
+ */
+void
+archimedes_convert_tree(cd9660node *node)
+{
+       cd9660node *cn;
+
+       assert(node != NULL);
+
+       archimedes_convert_node(node);
+
+               /* Recurse on children. */
+       TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
+               archimedes_convert_tree(cn);
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_archimedes.h b/usr.sbin/makefs/cd9660/cd9660_archimedes.h
new file mode 100644 (file)
index 0000000..61338d2
--- /dev/null
@@ -0,0 +1,48 @@
+/* $NetBSD: cd9660_archimedes.h,v 1.1 2009/01/10 22:06:29 bjh21 Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2009 Ben Harris
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+/*
+ * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension
+ */
+
+struct ISO_ARCHIMEDES {
+       char            magic[10];      /* "ARCHIMEDES" */
+       unsigned char   loadaddr[4];    /* Load address, little-endian */
+       unsigned char   execaddr[4];    /* Exec address, little-endian */
+       unsigned char   ro_attr;        /* RISC OS attributes */
+#define RO_ACCESS_UR   0x01 /* Owner read */
+#define RO_ACCESS_UW   0x02 /* Owner write */
+#define RO_ACCESS_L    0x04 /* Locked */
+#define RO_ACCESS_OR   0x10 /* Public read */
+#define RO_ACCESS_OW   0x20 /* Public write */
+       unsigned char   cdfs_attr;      /* Extra attributes for CDFS */
+#define CDFS_PLING     0x01    /* Filename begins with '!' */
+       char            reserved[12];
+};
+
+extern void archimedes_convert_tree(cd9660node *);
diff --git a/usr.sbin/makefs/cd9660/cd9660_conversion.c b/usr.sbin/makefs/cd9660/cd9660_conversion.c
new file mode 100644 (file)
index 0000000..ae1e954
--- /dev/null
@@ -0,0 +1,203 @@
+/*     $NetBSD: cd9660_conversion.c,v 1.4 2007/03/14 14:11:17 christos Exp $   */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+#include "cd9660.h"
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: cd9660_conversion.c,v 1.4 2007/03/14 14:11:17 christos Exp $");
+#endif  /* !__lint */
+
+
+static char cd9660_compute_gm_offset(time_t);
+
+#if 0
+static inline int
+cd9660_pad_even(length)
+int length;
+{
+       return length + (length & 0x01);
+}
+#endif
+
+/*
+* These can probably be implemented using a macro
+*/
+
+/* Little endian */
+void
+cd9660_721(uint16_t w, unsigned char *twochar)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+       w = bswap16(w);
+#endif
+       memcpy(twochar,&w,2);
+}
+
+void
+cd9660_731(uint32_t w, unsigned char *fourchar)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+       w = bswap32(w);
+#endif
+       memcpy(fourchar,&w,4);
+}
+
+/* Big endian */
+void
+cd9660_722(uint16_t w, unsigned char *twochar)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+       w = bswap16(w);
+#endif
+       memcpy(twochar,&w,2);
+}
+
+void
+cd9660_732(uint32_t w, unsigned char *fourchar)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+       w = bswap32(w);
+#endif
+       memcpy(fourchar,&w,4);
+}
+
+/**
+* Convert a dword into a double endian string of eight characters
+* @param int The double word to convert
+* @param char* The string to write the both endian double word to - It is assumed this is allocated and at least
+*              eight characters long
+*/
+void
+cd9660_bothendian_dword(uint32_t dw, unsigned char *eightchar)
+{
+       uint32_t le, be;
+#if BYTE_ORDER == LITTLE_ENDIAN
+       le = dw;
+       be = bswap32(dw);
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+       be = dw;
+       le = bswap32(dw);
+#endif
+       memcpy(eightchar, &le, 4);
+       memcpy((eightchar+4), &be, 4);
+}
+
+/**
+* Convert a word into a double endian string of four characters
+* @param int The word to convert
+* @param char* The string to write the both endian word to - It is assumed this is allocated and at least
+*              four characters long
+*/
+void
+cd9660_bothendian_word(uint16_t dw, unsigned char *fourchar)
+{
+       uint16_t le, be;
+#if BYTE_ORDER == LITTLE_ENDIAN
+       le = dw;
+       be = bswap16(dw);
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+       be = dw;
+       le = bswap16(dw);
+#endif
+       memcpy(fourchar, &le, 2);
+       memcpy((fourchar+2), &be, 2);
+}
+
+void
+cd9660_pad_string_spaces(char *str, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i ++) {
+               if (str[i] == '\0')
+                       str[i] = 0x20;
+       }
+}
+
+static char
+cd9660_compute_gm_offset(time_t tim)
+{
+       struct tm t, gm;
+
+       (void)localtime_r(&tim, &t);
+       (void)gmtime_r(&tim, &gm);
+       gm.tm_year -= t.tm_year;
+       gm.tm_yday -= t.tm_yday;
+       gm.tm_hour -= t.tm_hour;
+       gm.tm_min  -= t.tm_min;
+       if (gm.tm_year < 0)
+               gm.tm_yday = -1;
+       else if (gm.tm_year > 0)
+               gm.tm_yday = 1;
+
+       return (char)(-(gm.tm_min + 60* (24 * gm.tm_yday + gm.tm_hour)) / 15);
+}
+
+/* Long dates: 17 characters */
+void
+cd9660_time_8426(unsigned char *buf, time_t tim)
+{
+       struct tm t;
+       char temp[18];
+
+       (void)localtime_r(&tim, &t);
+       (void)snprintf(temp, sizeof(temp), "%04i%02i%02i%02i%02i%02i%02i",
+               1900+(int)t.tm_year,
+               (int)t.tm_mon+1,
+               (int)t.tm_mday,
+               (int)t.tm_hour,
+               (int)t.tm_min,
+               (int)t.tm_sec,
+               0);
+       (void)memcpy(buf, temp, 16);
+       buf[16] = cd9660_compute_gm_offset(tim);
+}
+
+/* Short dates: 7 characters */
+void
+cd9660_time_915(unsigned char *buf, time_t tim)
+{
+       struct tm t;
+
+       (void)localtime_r(&tim, &t);
+       buf[0] = t.tm_year;
+       buf[1] = t.tm_mon+1;
+       buf[2] = t.tm_mday;
+       buf[3] = t.tm_hour;
+       buf[4] = t.tm_min;
+       buf[5] = t.tm_sec;
+       buf[6] = cd9660_compute_gm_offset(tim);
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_debug.c b/usr.sbin/makefs/cd9660/cd9660_debug.c
new file mode 100644 (file)
index 0000000..33e2b3e
--- /dev/null
@@ -0,0 +1,498 @@
+/*     $NetBSD: cd9660_debug.c,v 1.13 2013/10/19 17:16:37 christos Exp $       */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: cd9660_debug.c,v 1.13 2013/10/19 17:16:37 christos Exp $");
+#endif  /* !__lint */
+
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/mount.h>
+#endif
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "iso9660_rrip.h"
+
+static void debug_print_susp_attrs(cd9660node *, int);
+static void debug_dump_to_xml_padded_hex_output(const char *, unsigned char *,
+                                               int);
+
+static inline void
+print_n_tabs(int n)
+{
+       int i;
+
+       for (i = 1; i <= n; i ++)
+               printf("\t");
+}
+
+#if 0
+void
+debug_print_rrip_info(n)
+cd9660node *n;
+{
+       struct ISO_SUSP_ATTRIBUTES *t;
+       TAILQ_FOREACH(t, &node->head, rr_ll) {
+
+       }
+}
+#endif
+
+static void
+debug_print_susp_attrs(cd9660node *n, int indent)
+{
+       struct ISO_SUSP_ATTRIBUTES *t;
+
+       TAILQ_FOREACH(t, &n->head, rr_ll) {
+               print_n_tabs(indent);
+               printf("-");
+               printf("%c%c: L:%i",t->attr.su_entry.SP.h.type[0],
+                   t->attr.su_entry.SP.h.type[1],
+                   (int)t->attr.su_entry.SP.h.length[0]);
+               printf("\n");
+       }
+}
+
+void
+debug_print_tree(iso9660_disk *diskStructure, cd9660node *node, int level)
+{
+#if !HAVE_NBTOOL_CONFIG_H
+       cd9660node *cn;
+
+       print_n_tabs(level);
+       if (node->type & CD9660_TYPE_DOT) {
+               printf(". (%i)\n",
+                   isonum_733(node->isoDirRecord->extent));
+       } else if (node->type & CD9660_TYPE_DOTDOT) {
+               printf("..(%i)\n",
+                   isonum_733(node->isoDirRecord->extent));
+       } else if (node->isoDirRecord->name[0]=='\0') {
+               printf("(ROOT) (%" PRIu32 " to %" PRId64 ")\n",
+                   node->fileDataSector,
+                   node->fileDataSector +
+                       node->fileSectorsUsed - 1);
+       } else {
+               printf("%s (%s) (%" PRIu32 " to %" PRId64 ")\n",
+                   node->isoDirRecord->name,
+                   (node->isoDirRecord->flags[0]
+                       & ISO_FLAG_DIRECTORY) ?  "DIR" : "FILE",
+                   node->fileDataSector,
+                   (node->fileSectorsUsed == 0) ?
+                       node->fileDataSector :
+                       node->fileDataSector
+                           + node->fileSectorsUsed - 1);
+       }
+       if (diskStructure->rock_ridge_enabled)
+               debug_print_susp_attrs(node, level + 1);
+       TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
+               debug_print_tree(diskStructure, cn, level + 1);
+#else
+       printf("Sorry, debugging is not supported in host-tools mode.\n");
+#endif
+}
+
+void
+debug_print_path_tree(cd9660node *n)
+{
+       cd9660node *iterator = n;
+
+       /* Only display this message when called with the root node */
+       if (n->parent == NULL)
+               printf("debug_print_path_table: Dumping path table contents\n");
+
+       while (iterator != NULL) {
+               if (iterator->isoDirRecord->name[0] == '\0')
+                       printf("0) (ROOT)\n");
+               else
+                       printf("%i) %s\n", iterator->level,
+                           iterator->isoDirRecord->name);
+
+               iterator = iterator->ptnext;
+       }
+}
+
+void
+debug_print_volume_descriptor_information(iso9660_disk *diskStructure)
+{
+       volume_descriptor *tmp = diskStructure->firstVolumeDescriptor;
+       char temp[CD9660_SECTOR_SIZE];
+
+       printf("==Listing Volume Descriptors==\n");
+
+       while (tmp != NULL) {
+               memset(temp, 0, CD9660_SECTOR_SIZE);
+               memcpy(temp, tmp->volumeDescriptorData + 1, 5);
+               printf("Volume descriptor in sector %" PRId64
+                   ": type %i, ID %s\n",
+                   tmp->sector, tmp->volumeDescriptorData[0], temp);
+               switch(tmp->volumeDescriptorData[0]) {
+               case 0:/*boot record*/
+                       break;
+
+               case 1:         /* PVD */
+                       break;
+
+               case 2:         /* SVD */
+                       break;
+
+               case 3:         /* Volume Partition Descriptor */
+                       break;
+
+               case 255:       /* terminator */
+                       break;
+               }
+               tmp = tmp->next;
+       }
+
+       printf("==Done Listing Volume Descriptors==\n");
+}
+
+void
+debug_dump_to_xml_ptentry(path_table_entry *pttemp, int num, int mode)
+{
+       printf("<ptentry num=\"%i\">\n" ,num);
+       printf("<length>%i</length>\n", pttemp->length[0]);
+       printf("<extended_attribute_length>%i</extended_attribute_length>\n",
+           pttemp->extended_attribute_length[0]);
+       printf("<parent_number>%i</parent_number>\n",
+           debug_get_encoded_number(pttemp->parent_number,mode));
+       debug_dump_to_xml_padded_hex_output("name",
+           pttemp->name, pttemp->length[0]);
+       printf("</ptentry>\n");
+}
+
+void
+debug_dump_to_xml_path_table(FILE *fd, off_t sector, int size, int mode)
+{
+       path_table_entry pttemp;
+       int t = 0;
+       int n = 0;
+
+       if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1)
+               err(1, "fseeko");
+
+       while (t < size) {
+               /* Read fixed data first */
+               fread(&pttemp, 1, 8, fd);
+               t += 8;
+               /* Read variable */
+               fread(((unsigned char*)&pttemp) + 8, 1, pttemp.length[0], fd);
+               t += pttemp.length[0];
+               debug_dump_to_xml_ptentry(&pttemp, n, mode);
+               n++;
+       }
+
+}
+
+/*
+ * XML Debug output functions
+ * Dump hierarchy of CD, as well as volume info, to XML
+ * Can be used later to diff against a standard,
+ * or just provide easy to read detailed debug output
+ */
+void
+debug_dump_to_xml(FILE *fd)
+{
+       unsigned char buf[CD9660_SECTOR_SIZE];
+       off_t sector;
+       int t, t2;
+       struct iso_primary_descriptor primaryVD;
+       struct _boot_volume_descriptor bootVD;
+
+       memset(&primaryVD, 0, sizeof(primaryVD));
+       printf("<cd9660dump>\n");
+
+       /* Display Volume Descriptors */
+       sector = 16;
+       do {
+               if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1)
+                       err(1, "fseeko");
+               fread(buf, 1, CD9660_SECTOR_SIZE, fd);
+               t = (int)((unsigned char)buf[0]);
+               switch (t) {
+               case 0:
+                       memcpy(&bootVD, buf, CD9660_SECTOR_SIZE);
+                       break;
+               case 1:
+                       memcpy(&primaryVD, buf, CD9660_SECTOR_SIZE);
+                       break;
+               }
+               debug_dump_to_xml_volume_descriptor(buf, sector);
+               sector++;
+       } while (t != 255);
+
+       t = debug_get_encoded_number((u_char *)primaryVD.type_l_path_table,
+           731);
+       t2 = debug_get_encoded_number((u_char *)primaryVD.path_table_size, 733);
+       printf("Path table 1 located at sector %i and is %i bytes long\n",
+           t,t2);
+       debug_dump_to_xml_path_table(fd, t, t2, 721);
+
+       t = debug_get_encoded_number((u_char *)primaryVD.type_m_path_table,
+           731);
+       debug_dump_to_xml_path_table(fd, t, t2, 722);
+
+       printf("</cd9660dump>\n");
+}
+
+static void
+debug_dump_to_xml_padded_hex_output(const char *element, unsigned char *buf,
+                                   int len)
+{
+       int i;
+       int t;
+
+       printf("<%s>",element);
+       for (i = 0; i < len; i++) {
+               t = (unsigned char)buf[i];
+               if (t >= 32 && t < 127)
+                       printf("%c",t);
+       }
+       printf("</%s>\n",element);
+
+       printf("<%s:hex>",element);
+       for (i = 0; i < len; i++) {
+               t = (unsigned char)buf[i];
+               printf(" %x",t);
+       }
+       printf("</%s:hex>\n",element);
+}
+
+int
+debug_get_encoded_number(unsigned char* buf, int mode)
+{
+#if !HAVE_NBTOOL_CONFIG_H
+       switch (mode) {
+       /* 711: Single bite */
+       case 711:
+               return isonum_711(buf);
+
+       /* 712: Single signed byte */
+       case 712:
+               return isonum_712((signed char *)buf);
+
+       /* 721: 16 bit LE */
+       case 721:
+               return isonum_721(buf);
+
+       /* 731: 32 bit LE */
+       case 731:
+               return isonum_731(buf);
+
+       /* 722: 16 bit BE */
+       case 722:
+               return isonum_722(buf);
+
+       /* 732: 32 bit BE */
+       case 732:
+               return isonum_732(buf);
+
+       /* 723: 16 bit bothE */
+       case 723:
+               return isonum_723(buf);
+
+       /* 733: 32 bit bothE */
+       case 733:
+               return isonum_733(buf);
+       }
+#endif
+       return 0;
+}
+
+void
+debug_dump_integer(const char *element, char* buf, int mode)
+{
+       printf("<%s>%i</%s>\n", element,
+           debug_get_encoded_number((unsigned char *)buf, mode), element);
+}
+
+void
+debug_dump_string(const char *element __unused, unsigned char *buf __unused, int len __unused)
+{
+
+}
+
+void
+debug_dump_directory_record_9_1(unsigned char* buf)
+{
+       printf("<directoryrecord>\n");
+       debug_dump_integer("length",
+           ((struct iso_directory_record*) buf)->length, 711);
+       debug_dump_integer("ext_attr_length",
+           ((struct iso_directory_record*) buf)->ext_attr_length,711);
+       debug_dump_integer("extent",
+           (char *)((struct iso_directory_record*) buf)->extent, 733);
+       debug_dump_integer("size",
+           (char *)((struct iso_directory_record*) buf)->size, 733);
+       debug_dump_integer("flags",
+           ((struct iso_directory_record*) buf)->flags, 711);
+       debug_dump_integer("file_unit_size",
+           ((struct iso_directory_record*) buf)->file_unit_size,711);
+       debug_dump_integer("interleave",
+           ((struct iso_directory_record*) buf)->interleave, 711);
+       debug_dump_integer("volume_sequence_number",
+           ((struct iso_directory_record*) buf)->volume_sequence_number,
+           723);
+       debug_dump_integer("name_len",
+           ((struct iso_directory_record*) buf)->name_len, 711);
+       debug_dump_to_xml_padded_hex_output("name",
+           (u_char *)((struct iso_directory_record*) buf)->name,
+               debug_get_encoded_number((u_char *)
+                   ((struct iso_directory_record*) buf)->length, 711));
+       printf("</directoryrecord>\n");
+}
+
+
+void
+debug_dump_to_xml_volume_descriptor(unsigned char* buf, int sector)
+{
+       printf("<volumedescriptor sector=\"%i\">\n", sector);
+       printf("<vdtype>");
+       switch(buf[0]) {
+       case 0:
+               printf("boot");
+               break;
+
+       case 1:
+               printf("primary");
+               break;
+
+       case 2:
+               printf("supplementary");
+               break;
+
+       case 3:
+               printf("volume partition descriptor");
+               break;
+
+       case 255:
+               printf("terminator");
+               break;
+       }
+
+       printf("</vdtype>\n");
+       switch(buf[0]) {
+       case 1:
+               debug_dump_integer("type",
+                   ((struct iso_primary_descriptor*)buf)->type, 711);
+               debug_dump_to_xml_padded_hex_output("id",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->id,
+                   ISODCL (  2,   6));
+               debug_dump_integer("version",
+                   ((struct iso_primary_descriptor*)buf)->version,
+                    711);
+               debug_dump_to_xml_padded_hex_output("system_id",
+                   (u_char *)((struct iso_primary_descriptor*)buf)->system_id,
+                   ISODCL(9,40));
+               debug_dump_to_xml_padded_hex_output("volume_id",
+                   (u_char *)((struct iso_primary_descriptor*)buf)->volume_id,
+                   ISODCL(41,72));
+               debug_dump_integer("volume_space_size",
+                   ((struct iso_primary_descriptor*)buf)->volume_space_size,
+                   733);
+               debug_dump_integer("volume_set_size",
+                   ((struct iso_primary_descriptor*)buf)->volume_set_size,
+                           733);
+               debug_dump_integer("volume_sequence_number",
+                   ((struct iso_primary_descriptor*)buf)->volume_sequence_number,
+                   723);
+               debug_dump_integer("logical_block_size",
+                   ((struct iso_primary_descriptor*)buf)->logical_block_size,
+                           723);
+               debug_dump_integer("path_table_size",
+                   ((struct iso_primary_descriptor*)buf)->path_table_size,
+                           733);
+               debug_dump_integer("type_l_path_table",
+                   ((struct iso_primary_descriptor*)buf)->type_l_path_table,
+                   731);
+               debug_dump_integer("opt_type_l_path_table",
+                   ((struct iso_primary_descriptor*)buf)->opt_type_l_path_table,
+                   731);
+               debug_dump_integer("type_m_path_table",
+                   ((struct iso_primary_descriptor*)buf)->type_m_path_table,
+                   732);
+               debug_dump_integer("opt_type_m_path_table",
+                       ((struct iso_primary_descriptor*)buf)->opt_type_m_path_table,732);
+               debug_dump_directory_record_9_1(
+                   (u_char *)((struct iso_primary_descriptor*)buf)->root_directory_record);
+               debug_dump_to_xml_padded_hex_output("volume_set_id",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->volume_set_id,
+                   ISODCL (191, 318));
+               debug_dump_to_xml_padded_hex_output("publisher_id",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->publisher_id,
+                   ISODCL (319, 446));
+               debug_dump_to_xml_padded_hex_output("preparer_id",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->preparer_id,
+                   ISODCL (447, 574));
+               debug_dump_to_xml_padded_hex_output("application_id",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->application_id,
+                   ISODCL (575, 702));
+               debug_dump_to_xml_padded_hex_output("copyright_file_id",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->copyright_file_id,
+                   ISODCL (703, 739));
+               debug_dump_to_xml_padded_hex_output("abstract_file_id",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->abstract_file_id,
+                   ISODCL (740, 776));
+               debug_dump_to_xml_padded_hex_output("bibliographic_file_id",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->bibliographic_file_id,
+                   ISODCL (777, 813));
+
+               debug_dump_to_xml_padded_hex_output("creation_date",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->creation_date,
+                   ISODCL (814, 830));
+               debug_dump_to_xml_padded_hex_output("modification_date",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->modification_date,
+                   ISODCL (831, 847));
+               debug_dump_to_xml_padded_hex_output("expiration_date",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->expiration_date,
+                   ISODCL (848, 864));
+               debug_dump_to_xml_padded_hex_output("effective_date",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->effective_date,
+                   ISODCL (865, 881));
+
+               debug_dump_to_xml_padded_hex_output("file_structure_version",
+                   (u_char *)((struct iso_primary_descriptor*) buf)->file_structure_version,
+                   ISODCL(882,882));
+               break;
+       }
+       printf("</volumedescriptor>\n");
+}
+
diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.c b/usr.sbin/makefs/cd9660/cd9660_eltorito.c
new file mode 100644 (file)
index 0000000..dc93425
--- /dev/null
@@ -0,0 +1,698 @@
+/*     $NetBSD: cd9660_eltorito.c,v 1.20 2013/01/28 21:03:28 christos Exp $    */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+
+
+#include "cd9660.h"
+#include "cd9660_eltorito.h"
+#include <sys/bootblock.h>
+#include <util.h>
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: cd9660_eltorito.c,v 1.20 2013/01/28 21:03:28 christos Exp $");
+#endif  /* !__lint */
+
+#ifdef DEBUG
+#define        ELTORITO_DPRINTF(__x)   printf __x
+#else
+#define        ELTORITO_DPRINTF(__x)
+#endif
+
+#include <util.h>
+
+static struct boot_catalog_entry *cd9660_init_boot_catalog_entry(void);
+static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
+static struct boot_catalog_entry *cd9660_boot_setup_default_entry(
+    struct cd9660_boot_image *);
+static struct boot_catalog_entry *cd9660_boot_setup_section_head(char);
+static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
+#if 0
+static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *);
+#endif
+
+int
+cd9660_add_boot_disk(iso9660_disk *diskStructure, const char *boot_info)
+{
+       struct stat stbuf;
+       const char *mode_msg;
+       char *temp;
+       char *sysname;
+       char *filename;
+       struct cd9660_boot_image *new_image, *tmp_image;
+
+       assert(boot_info != NULL);
+
+       if (*boot_info == '\0') {
+               warnx("Error: Boot disk information must be in the "
+                     "format 'system;filename'");
+               return 0;
+       }
+
+       /* First decode the boot information */
+       temp = estrdup(boot_info);
+
+       sysname = temp;
+       filename = strchr(sysname, ';');
+       if (filename == NULL) {
+               warnx("supply boot disk information in the format "
+                   "'system;filename'");
+               free(temp);
+               return 0;
+       }
+
+       *filename++ = '\0';
+
+       if (diskStructure->verbose_level > 0) {
+               printf("Found bootdisk with system %s, and filename %s\n",
+                   sysname, filename);
+       }
+       new_image = ecalloc(1, sizeof(*new_image));
+       new_image->loadSegment = 0;     /* default for now */
+
+       /* Decode System */
+       if (strcmp(sysname, "i386") == 0)
+               new_image->system = ET_SYS_X86;
+       else if (strcmp(sysname, "powerpc") == 0)
+               new_image->system = ET_SYS_PPC;
+       else if (strcmp(sysname, "macppc") == 0 ||
+                strcmp(sysname, "mac68k") == 0)
+               new_image->system = ET_SYS_MAC;
+       else {
+               warnx("boot disk system must be "
+                     "i386, powerpc, macppc, or mac68k");
+               free(temp);
+               free(new_image);
+               return 0;
+       }
+
+
+       new_image->filename = estrdup(filename);
+
+       free(temp);
+
+       /* Get information about the file */
+       if (lstat(new_image->filename, &stbuf) == -1)
+               err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
+                   new_image->filename);
+
+       switch (stbuf.st_size) {
+       case 1440 * 1024:
+               new_image->targetMode = ET_MEDIA_144FDD;
+               mode_msg = "Assigned boot image to 1.44 emulation mode";
+               break;
+       case 1200 * 1024:
+               new_image->targetMode = ET_MEDIA_12FDD;
+               mode_msg = "Assigned boot image to 1.2 emulation mode";
+               break;
+       case 2880 * 1024:
+               new_image->targetMode = ET_MEDIA_288FDD;
+               mode_msg = "Assigned boot image to 2.88 emulation mode";
+               break;
+       default:
+               new_image->targetMode = ET_MEDIA_NOEM;
+               mode_msg = "Assigned boot image to no emulation mode";
+               break;
+       }
+
+       if (diskStructure->verbose_level > 0)
+               printf("%s\n", mode_msg);
+
+       new_image->size = stbuf.st_size;
+       new_image->num_sectors =
+           howmany(new_image->size, diskStructure->sectorSize) *
+           howmany(diskStructure->sectorSize, 512);
+       if (diskStructure->verbose_level > 0) {
+               printf("New image has size %d, uses %d 512-byte sectors\n",
+                   new_image->size, new_image->num_sectors);
+       }
+       new_image->sector = -1;
+       /* Bootable by default */
+       new_image->bootable = ET_BOOTABLE;
+       /* Add boot disk */
+
+       /* Group images for the same platform together. */
+       TAILQ_FOREACH(tmp_image, &diskStructure->boot_images, image_list) {
+               if (tmp_image->system != new_image->system)
+                       break;
+       }
+
+       if (tmp_image == NULL) {
+               TAILQ_INSERT_HEAD(&diskStructure->boot_images, new_image,
+                   image_list);
+       } else
+               TAILQ_INSERT_BEFORE(tmp_image, new_image, image_list);
+
+       new_image->serialno = diskStructure->image_serialno++;
+
+       /* TODO : Need to do anything about the boot image in the tree? */
+       diskStructure->is_bootable = 1;
+
+       return 1;
+}
+
+int
+cd9660_eltorito_add_boot_option(iso9660_disk *diskStructure,
+    const char *option_string, const char *value)
+{
+       char *eptr;
+       struct cd9660_boot_image *image;
+
+       assert(option_string != NULL);
+
+       /* Find the last image added */
+       TAILQ_FOREACH(image, &diskStructure->boot_images, image_list) {
+               if (image->serialno + 1 == diskStructure->image_serialno)
+                       break;
+       }
+       if (image == NULL)
+               errx(EXIT_FAILURE, "Attempted to add boot option, "
+                   "but no boot images have been specified");
+
+       if (strcmp(option_string, "no-emul-boot") == 0) {
+               image->targetMode = ET_MEDIA_NOEM;
+       } else if (strcmp(option_string, "no-boot") == 0) {
+               image->bootable = ET_NOT_BOOTABLE;
+       } else if (strcmp(option_string, "hard-disk-boot") == 0) {
+               image->targetMode = ET_MEDIA_HDD;
+       } else if (strcmp(option_string, "boot-load-segment") == 0) {
+               image->loadSegment = strtoul(value, &eptr, 16);
+               if (eptr == value || *eptr != '\0' || errno != ERANGE) {
+                       warn("%s: strtoul", __func__);
+                       return 0;
+               }
+       } else {
+               return 0;
+       }
+       return 1;
+}
+
+static struct boot_catalog_entry *
+cd9660_init_boot_catalog_entry(void)
+{
+       return ecalloc(1, sizeof(struct boot_catalog_entry));
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_validation_entry(char sys)
+{
+       struct boot_catalog_entry *entry;
+       boot_catalog_validation_entry *ve;
+       int16_t checksum;
+       unsigned char *csptr;
+       size_t i;
+       entry = cd9660_init_boot_catalog_entry();
+
+       ve = &entry->entry_data.VE;
+
+       ve->header_id[0] = 1;
+       ve->platform_id[0] = sys;
+       ve->key[0] = 0x55;
+       ve->key[1] = 0xAA;
+
+       /* Calculate checksum */
+       checksum = 0;
+       cd9660_721(0, ve->checksum);
+       csptr = (unsigned char*)ve;
+       for (i = 0; i < sizeof(*ve); i += 2) {
+               checksum += (int16_t)csptr[i];
+               checksum += 256 * (int16_t)csptr[i + 1];
+       }
+       checksum = -checksum;
+       cd9660_721(checksum, ve->checksum);
+
+        ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, "
+           "checksum %04x\n", __func__, ve->header_id[0], ve->platform_id[0],
+           ve->key[0], ve->key[1], checksum));
+       return entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk)
+{
+       struct boot_catalog_entry *default_entry;
+       boot_catalog_initial_entry *ie;
+
+       default_entry = cd9660_init_boot_catalog_entry();
+       if (default_entry == NULL)
+               return NULL;
+
+       ie = &default_entry->entry_data.IE;
+
+       ie->boot_indicator[0] = disk->bootable;
+       ie->media_type[0] = disk->targetMode;
+       cd9660_721(disk->loadSegment, ie->load_segment);
+       ie->system_type[0] = disk->system;
+       cd9660_721(disk->num_sectors, ie->sector_count);
+       cd9660_731(disk->sector, ie->load_rba);
+
+       ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, "
+           "load segment %04x, system type %d, sector count %d, "
+           "load rba %d\n", __func__, ie->boot_indicator[0],
+           ie->media_type[0], disk->loadSegment, ie->system_type[0],
+           disk->num_sectors, disk->sector));
+       return default_entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_section_head(char platform)
+{
+       struct boot_catalog_entry *entry;
+       boot_catalog_section_header *sh;
+
+       entry = cd9660_init_boot_catalog_entry();
+       if (entry == NULL)
+               return NULL;
+
+       sh = &entry->entry_data.SH;
+       /* More by default. The last one will manually be set to 0x91 */
+       sh->header_indicator[0] = ET_SECTION_HEADER_MORE;
+       sh->platform_id[0] = platform;
+       sh->num_section_entries[0] = 0;
+       return entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk)
+{
+       struct boot_catalog_entry *entry;
+       boot_catalog_section_entry *se;
+       if ((entry = cd9660_init_boot_catalog_entry()) == NULL)
+               return NULL;
+
+       se = &entry->entry_data.SE;
+
+       se->boot_indicator[0] = ET_BOOTABLE;
+       se->media_type[0] = disk->targetMode;
+       cd9660_721(disk->loadSegment, se->load_segment);
+       cd9660_721(disk->num_sectors, se->sector_count);
+       cd9660_731(disk->sector, se->load_rba);
+       return entry;
+}
+
+#if 0
+static u_char
+cd9660_boot_get_system_type(struct cd9660_boot_image *disk)
+{
+       /*
+               For hard drive booting, we need to examine the MBR to figure
+               out what the partition type is
+       */
+       return 0;
+}
+#endif
+
+/*
+ * Set up the BVD, Boot catalog, and the boot entries, but do no writing
+ */
+int
+cd9660_setup_boot(iso9660_disk *diskStructure, int first_sector)
+{
+       int sector;
+       int used_sectors;
+       int num_entries = 0;
+       int catalog_sectors;
+       struct boot_catalog_entry *x86_head, *mac_head, *ppc_head,
+               *valid_entry, *default_entry, *temp, *head, **headp, *next;
+       struct cd9660_boot_image *tmp_disk;
+
+       headp = NULL;
+       x86_head = mac_head = ppc_head = NULL;
+
+       /* If there are no boot disks, don't bother building boot information */
+       if (TAILQ_EMPTY(&diskStructure->boot_images))
+               return 0;
+
+       /* Point to catalog: For now assume it consumes one sector */
+       ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector));
+       diskStructure->boot_catalog_sector = first_sector;
+       cd9660_bothendian_dword(first_sector,
+               diskStructure->boot_descriptor->boot_catalog_pointer);
+
+       /* Step 1: Generate boot catalog */
+       /* Step 1a: Validation entry */
+       valid_entry = cd9660_boot_setup_validation_entry(ET_SYS_X86);
+       if (valid_entry == NULL)
+               return -1;
+
+       /*
+        * Count how many boot images there are,
+        * and how many sectors they consume.
+        */
+       num_entries = 1;
+       used_sectors = 0;
+
+       TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) {
+               used_sectors += tmp_disk->num_sectors;
+
+               /* One default entry per image */
+               num_entries++;
+       }
+       catalog_sectors = howmany(num_entries * 0x20, diskStructure->sectorSize);
+       used_sectors += catalog_sectors;
+
+       if (diskStructure->verbose_level > 0) {
+               printf("%s: there will be %i entries consuming %i sectors. "
+                      "Catalog is %i sectors\n", __func__, num_entries,
+                      used_sectors, catalog_sectors);
+       }
+
+       /* Populate sector numbers */
+       sector = first_sector + catalog_sectors;
+       TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) {
+               tmp_disk->sector = sector;
+               sector += tmp_disk->num_sectors;
+       }
+
+       LIST_INSERT_HEAD(&diskStructure->boot_entries, valid_entry, ll_struct);
+
+       /* Step 1b: Initial/default entry */
+       /* TODO : PARAM */
+       tmp_disk = TAILQ_FIRST(&diskStructure->boot_images);
+       default_entry = cd9660_boot_setup_default_entry(tmp_disk);
+       if (default_entry == NULL) {
+               warnx("Error: memory allocation failed in cd9660_setup_boot");
+               return -1;
+       }
+
+       LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct);
+
+       /* Todo: multiple default entries? */
+
+       tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
+
+       temp = default_entry;
+
+       /* If multiple boot images are given : */
+       while (tmp_disk != NULL) {
+               /* Step 2: Section header */
+               switch (tmp_disk->system) {
+               case ET_SYS_X86:
+                       headp = &x86_head;
+                       break;
+               case ET_SYS_PPC:
+                       headp = &ppc_head;
+                       break;
+               case ET_SYS_MAC:
+                       headp = &mac_head;
+                       break;
+               default:
+                       warnx("%s: internal error: unknown system type",
+                           __func__);
+                       return -1;
+               }
+
+               if (*headp == NULL) {
+                       head =
+                           cd9660_boot_setup_section_head(tmp_disk->system);
+                       if (head == NULL) {
+                               warnx("Error: memory allocation failed in "
+                                     "cd9660_setup_boot");
+                               return -1;
+                       }
+                       LIST_INSERT_AFTER(default_entry, head, ll_struct);
+                       *headp = head;
+               } else
+                       head = *headp;
+
+               head->entry_data.SH.num_section_entries[0]++;
+
+               /* Step 2a: Section entry and extensions */
+               temp = cd9660_boot_setup_section_entry(tmp_disk);
+               if (temp == NULL) {
+                       warn("%s: cd9660_boot_setup_section_entry", __func__);
+                       return -1;
+               }
+
+               while ((next = LIST_NEXT(head, ll_struct)) != NULL &&
+                      next->entry_type == ET_ENTRY_SE)
+                       head = next;
+
+               LIST_INSERT_AFTER(head, temp, ll_struct);
+               tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
+       }
+
+       /* TODO: Remaining boot disks when implemented */
+
+       return first_sector + used_sectors;
+}
+
+int
+cd9660_setup_boot_volume_descriptor(iso9660_disk *diskStructure,
+    volume_descriptor *bvd)
+{
+       boot_volume_descriptor *bvdData =
+           (boot_volume_descriptor*)bvd->volumeDescriptorData;
+
+       bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT;
+       memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+       bvdData->version[0] = 1;
+       memcpy(bvdData->boot_system_identifier, ET_ID, 23);
+       memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+       diskStructure->boot_descriptor =
+           (boot_volume_descriptor*) bvd->volumeDescriptorData;
+       return 1;
+}
+
+static int
+cd9660_write_mbr_partition_entry(FILE *fd, int idx, off_t sector_start,
+    off_t nsectors, int type)
+{
+       uint8_t val;
+       uint32_t lba;
+
+       if (fseeko(fd, (off_t)(idx) * 16 + 0x1be, SEEK_SET) == -1)
+               err(1, "fseeko");
+       
+       val = 0x80; /* Bootable */
+       fwrite(&val, sizeof(val), 1, fd);
+
+       val = 0xff; /* CHS begin */
+       fwrite(&val, sizeof(val), 1, fd);
+       fwrite(&val, sizeof(val), 1, fd);
+       fwrite(&val, sizeof(val), 1, fd);
+
+       val = type; /* Part type */
+       fwrite(&val, sizeof(val), 1, fd);
+
+       val = 0xff; /* CHS end */
+       fwrite(&val, sizeof(val), 1, fd);
+       fwrite(&val, sizeof(val), 1, fd);
+       fwrite(&val, sizeof(val), 1, fd);
+
+       /* LBA extent */
+       lba = htole32(sector_start);
+       fwrite(&lba, sizeof(lba), 1, fd);
+       lba = htole32(nsectors);
+       fwrite(&lba, sizeof(lba), 1, fd);
+
+       return 0;
+}
+
+static int
+cd9660_write_apm_partition_entry(FILE *fd, int idx, int total_partitions,
+    off_t sector_start, off_t nsectors, off_t sector_size,
+    const char *part_name, const char *part_type)
+{
+       uint32_t apm32, part_status;
+       uint16_t apm16;
+
+       /* See Apple Tech Note 1189 for the details about the pmPartStatus
+        * flags.
+        * Below the flags which are default:
+        * - IsValid     0x01
+        * - IsAllocated 0x02
+        * - IsReadable  0x10
+        * - IsWritable  0x20
+        */
+       part_status = APPLE_PS_VALID | APPLE_PS_ALLOCATED | APPLE_PS_READABLE |
+           APPLE_PS_WRITABLE;
+
+       if (fseeko(fd, (off_t)(idx + 1) * sector_size, SEEK_SET) == -1)
+               err(1, "fseeko");
+
+       /* Signature */
+       apm16 = htobe16(0x504d);
+       fwrite(&apm16, sizeof(apm16), 1, fd);
+       apm16 = 0;
+       fwrite(&apm16, sizeof(apm16), 1, fd);
+
+       /* Total number of partitions */
+       apm32 = htobe32(total_partitions);
+       fwrite(&apm32, sizeof(apm32), 1, fd);
+       /* Bounds */
+       apm32 = htobe32(sector_start);
+       fwrite(&apm32, sizeof(apm32), 1, fd);
+       apm32 = htobe32(nsectors);
+       fwrite(&apm32, sizeof(apm32), 1, fd);
+
+       fwrite(part_name, strlen(part_name) + 1, 1, fd);
+       fseek(fd, 32 - strlen(part_name) - 1, SEEK_CUR);
+       fwrite(part_type, strlen(part_type) + 1, 1, fd);
+       fseek(fd, 32 - strlen(part_type) - 1, SEEK_CUR);
+
+       apm32 = 0;
+       /* pmLgDataStart */
+       fwrite(&apm32, sizeof(apm32), 1, fd);
+       /* pmDataCnt */ 
+       apm32 = htobe32(nsectors);
+       fwrite(&apm32, sizeof(apm32), 1, fd);
+       /* pmPartStatus */
+       apm32 = htobe32(part_status);
+       fwrite(&apm32, sizeof(apm32), 1, fd);
+
+       return 0;
+}
+
+int
+cd9660_write_boot(iso9660_disk *diskStructure, FILE *fd)
+{
+       struct boot_catalog_entry *e;
+       struct cd9660_boot_image *t;
+       int apm_partitions = 0;
+       int mbr_partitions = 0;
+
+       /* write boot catalog */
+       if (fseeko(fd, (off_t)diskStructure->boot_catalog_sector *
+           diskStructure->sectorSize, SEEK_SET) == -1)
+               err(1, "fseeko");
+
+       if (diskStructure->verbose_level > 0) {
+               printf("Writing boot catalog to sector %" PRId64 "\n",
+                   diskStructure->boot_catalog_sector);
+       }
+       LIST_FOREACH(e, &diskStructure->boot_entries, ll_struct) {
+               if (diskStructure->verbose_level > 0) {
+                       printf("Writing catalog entry of type %d\n",
+                           e->entry_type);
+               }
+               /*
+                * It doesnt matter which one gets written
+                * since they are the same size
+                */
+               fwrite(&(e->entry_data.VE), 1, 32, fd);
+       }
+       if (diskStructure->verbose_level > 0)
+               printf("Finished writing boot catalog\n");
+
+       /* copy boot images */
+       TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
+               if (diskStructure->verbose_level > 0) {
+                       printf("Writing boot image from %s to sectors %d\n",
+                           t->filename, t->sector);
+               }
+               cd9660_copy_file(diskStructure, fd, t->sector, t->filename);
+
+               if (t->system == ET_SYS_MAC) 
+                       apm_partitions++;
+               if (t->system == ET_SYS_PPC) 
+                       mbr_partitions++;
+       }
+
+       /* some systems need partition tables as well */
+       if (mbr_partitions > 0 || diskStructure->chrp_boot) {
+               uint16_t sig;
+
+               fseek(fd, 0x1fe, SEEK_SET);
+               sig = htole16(0xaa55);
+               fwrite(&sig, sizeof(sig), 1, fd);
+
+               mbr_partitions = 0;
+
+               /* Write ISO9660 descriptor, enclosing the whole disk */
+               if (diskStructure->chrp_boot)
+                       cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
+                           0, diskStructure->totalSectors *
+                           (diskStructure->sectorSize / 512), 0x96);
+
+               /* Write all partition entries */
+               TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
+                       if (t->system != ET_SYS_PPC)
+                               continue;
+                       cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
+                           t->sector * (diskStructure->sectorSize / 512),
+                           t->num_sectors * (diskStructure->sectorSize / 512),
+                           0x41 /* PReP Boot */);
+               }
+       }
+
+       if (apm_partitions > 0) {
+               /* Write DDR and global APM info */
+               uint32_t apm32;
+               uint16_t apm16;
+               int total_parts;
+
+               fseek(fd, 0, SEEK_SET);
+               apm16 = htobe16(0x4552);
+               fwrite(&apm16, sizeof(apm16), 1, fd);
+               /* Device block size */
+               apm16 = htobe16(512);
+               fwrite(&apm16, sizeof(apm16), 1, fd);
+               /* Device block count */
+               apm32 = htobe32(diskStructure->totalSectors *
+                   (diskStructure->sectorSize / 512));
+               fwrite(&apm32, sizeof(apm32), 1, fd);
+               /* Device type/id */
+               apm16 = htobe16(1);
+               fwrite(&apm16, sizeof(apm16), 1, fd);
+               fwrite(&apm16, sizeof(apm16), 1, fd);
+
+               /* Count total needed entries */
+               total_parts = 2 + apm_partitions; /* Self + ISO9660 */
+
+               /* Write self-descriptor */
+               cd9660_write_apm_partition_entry(fd, 0, total_parts, 1,
+                   total_parts, 512, "Apple", "Apple_partition_map");
+
+               /* Write all partition entries */
+               apm_partitions = 0;
+               TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
+                       if (t->system != ET_SYS_MAC)
+                               continue;
+
+                       cd9660_write_apm_partition_entry(fd,
+                           1 + apm_partitions++, total_parts,
+                           t->sector * (diskStructure->sectorSize / 512),
+                           t->num_sectors * (diskStructure->sectorSize / 512),
+                           512, "CD Boot", "Apple_Bootstrap");
+               }
+
+               /* Write ISO9660 descriptor, enclosing the whole disk */
+               cd9660_write_apm_partition_entry(fd, 2 + apm_partitions,
+                   total_parts, 0, diskStructure->totalSectors *
+                   (diskStructure->sectorSize / 512), 512, "ISO9660",
+                   "CD_ROM_Mode_1");
+       }
+
+       return 0;
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.h b/usr.sbin/makefs/cd9660/cd9660_eltorito.h
new file mode 100644 (file)
index 0000000..2661483
--- /dev/null
@@ -0,0 +1,162 @@
+/*     $NetBSD: cd9660_eltorito.h,v 1.5 2009/07/04 14:31:38 ahoka Exp $        */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+
+#ifndef _CD9660_ELTORITO_H_
+#define _CD9660_ELTORITO_H_
+
+/* Boot defines */
+#define        ET_ID           "EL TORITO SPECIFICATION"
+#define        ET_SYS_X86      0
+#define        ET_SYS_PPC      1
+#define        ET_SYS_MAC      2
+
+#define ET_BOOT_ENTRY_SIZE 0x20
+
+#define        ET_BOOTABLE             0x88
+#define        ET_NOT_BOOTABLE 0
+
+#define        ET_MEDIA_NOEM   0
+#define        ET_MEDIA_12FDD                  1
+#define        ET_MEDIA_144FDD                 2
+#define        ET_MEDIA_288FDD                 3
+#define        ET_MEDIA_HDD                    4
+
+#define ET_INDICATOR_HEADERMORE        0x90
+#define ET_INDICATOR_HEADERLAST        0x91
+#define ET_INDICATOR_EXTENSION 0x44
+
+/*** Boot Structures ***/
+
+typedef struct _boot_volume_descriptor {
+       u_char boot_record_indicator    [ISODCL(0x00,0x00)];
+       u_char identifier               [ISODCL(0x01,0x05)];
+       u_char version                  [ISODCL(0x06,0x06)];
+       u_char boot_system_identifier   [ISODCL(0x07,0x26)];
+       u_char unused1                  [ISODCL(0x27,0x46)];
+       u_char boot_catalog_pointer     [ISODCL(0x47,0x4A)];
+       u_char unused2                  [ISODCL(0x4B,0x7FF)];
+} boot_volume_descriptor;
+
+typedef struct _boot_catalog_validation_entry {
+       u_char header_id                [ISODCL(0x00,0x00)];
+       u_char platform_id              [ISODCL(0x01,0x01)];
+       u_char reserved1                [ISODCL(0x02,0x03)];
+       u_char manufacturer             [ISODCL(0x04,0x1B)];
+       u_char checksum                 [ISODCL(0x1C,0x1D)];
+       u_char key                      [ISODCL(0x1E,0x1F)];
+} boot_catalog_validation_entry;
+
+typedef struct _boot_catalog_initial_entry {
+       u_char boot_indicator           [ISODCL(0x00,0x00)];
+       u_char media_type               [ISODCL(0x01,0x01)];
+       u_char load_segment             [ISODCL(0x02,0x03)];
+       u_char system_type              [ISODCL(0x04,0x04)];
+       u_char unused_1                 [ISODCL(0x05,0x05)];
+       u_char sector_count             [ISODCL(0x06,0x07)];
+       u_char load_rba                 [ISODCL(0x08,0x0B)];
+       u_char unused_2                 [ISODCL(0x0C,0x1F)];
+} boot_catalog_initial_entry;
+
+#define ET_SECTION_HEADER_MORE         0x90
+#define ET_SECTION_HEADER_LAST         0x91
+
+typedef struct _boot_catalog_section_header {
+       u_char header_indicator         [ISODCL(0x00,0x00)];
+       u_char platform_id              [ISODCL(0x01,0x01)];
+       u_char num_section_entries      [ISODCL(0x02,0x03)];
+       u_char id_string                [ISODCL(0x04,0x1F)];
+} boot_catalog_section_header;
+
+typedef struct _boot_catalog_section_entry {
+       u_char boot_indicator           [ISODCL(0x00,0x00)];
+       u_char media_type               [ISODCL(0x01,0x01)];
+       u_char load_segment             [ISODCL(0x02,0x03)];
+       u_char system_type              [ISODCL(0x04,0x04)];
+       u_char unused_1                 [ISODCL(0x05,0x05)];
+       u_char sector_count             [ISODCL(0x06,0x07)];
+       u_char load_rba                 [ISODCL(0x08,0x0B)];
+       u_char selection_criteria       [ISODCL(0x0C,0x0C)];
+       u_char vendor_criteria          [ISODCL(0x0D,0x1F)];
+} boot_catalog_section_entry;
+
+typedef struct _boot_catalog_section_entry_extension {
+       u_char extension_indicator      [ISODCL(0x00,0x00)];
+       u_char flags                    [ISODCL(0x01,0x01)];
+       u_char vendor_criteria          [ISODCL(0x02,0x1F)];
+} boot_catalog_section_entry_extension;
+
+#define ET_ENTRY_VE 1
+#define ET_ENTRY_IE 2
+#define ET_ENTRY_SH 3
+#define ET_ENTRY_SE 4
+#define ET_ENTRY_EX 5
+
+struct boot_catalog_entry {
+       char entry_type;
+       union {
+               boot_catalog_validation_entry           VE;
+               boot_catalog_initial_entry              IE;
+               boot_catalog_section_header             SH;
+               boot_catalog_section_entry              SE;
+               boot_catalog_section_entry_extension    EX;
+       } entry_data;
+
+       LIST_ENTRY(boot_catalog_entry) ll_struct;
+};
+
+/* Temporary structure */
+struct cd9660_boot_image {
+       char *filename;
+       int size;
+       int sector;                     /* copied to LoadRBA */
+       int num_sectors;
+       unsigned int loadSegment;
+       u_char targetMode;
+       u_char system;
+       u_char bootable;
+       /*
+        * If the boot image exists in the filesystem
+        * already, this is a pointer to that node. For the sake
+        * of simplicity in future versions, this pointer is only
+        * to the node in the primary volume. This SHOULD be done
+        * via a hashtable lookup.
+        */
+       struct _cd9660node *boot_image_node;
+       TAILQ_ENTRY(cd9660_boot_image) image_list;
+       int serialno;
+};
+
+
+#endif /* _CD9660_ELTORITO_H_ */
+
diff --git a/usr.sbin/makefs/cd9660/cd9660_strings.c b/usr.sbin/makefs/cd9660/cd9660_strings.c
new file mode 100644 (file)
index 0000000..fa82274
--- /dev/null
@@ -0,0 +1,127 @@
+/*     $NetBSD: cd9660_strings.c,v 1.5 2011/03/23 13:11:51 christos Exp $      */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#else
+#include <sys/mount.h>
+#endif
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <ctype.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: cd9660_strings.c,v 1.5 2011/03/23 13:11:51 christos Exp $");
+#endif  /* !__lint */
+
+
+void
+cd9660_uppercase_characters(char *str, int len)
+{
+       int p;
+
+       for (p = 0; p < len; p++) {
+               if (islower((unsigned char)str[p]) )
+                       str[p] -= 32;
+       }
+}
+
+static inline int
+cd9660_is_d_char(char c)
+{
+       return (isupper((unsigned char)c)
+               || c == '_'
+               || (c >= '0' && c <= '9'));
+}
+
+static inline int
+cd9660_is_a_char(char c)
+{
+       return (isupper((unsigned char)c)
+                       || c == '_'
+                       || (c >= '%' && c <= '?')
+                       || (c >= ' ' && c <= '\"'));
+}
+
+/*
+ * Test a string to see if it is composed of valid a characters
+ * @param const char* The string to test
+ * @returns int 1 if valid, 2 if valid if characters are converted to
+ *              upper case, 0 otherwise
+ */
+int
+cd9660_valid_a_chars(const char *str)
+{
+       const char *c = str;
+       int upperFound = 0;
+
+       while ((*c) != '\0') {
+               if (!(cd9660_is_a_char(*c))) {
+                       if (islower((unsigned char)*c) )
+                               upperFound = 1;
+                       else
+                               return 0;
+               }
+               c++;
+       }
+       return upperFound + 1;
+}
+
+/*
+ * Test a string to see if it is composed of valid d characters
+ * @param const char* The string to test
+ * @returns int 1 if valid, 2 if valid if characters are converted to
+ *                upper case, 0 otherwise
+ */
+int
+cd9660_valid_d_chars(const char *str)
+{
+       const char *c=str;
+       int upperFound = 0;
+
+       while ((*c) != '\0') {
+               if (!(cd9660_is_d_char(*c))) {
+                       if (islower((unsigned char)*c) )
+                               upperFound = 1;
+                       else
+                               return 0;
+               }
+               c++;
+       }
+       return upperFound + 1;
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_write.c b/usr.sbin/makefs/cd9660/cd9660_write.c
new file mode 100644 (file)
index 0000000..edb4ba4
--- /dev/null
@@ -0,0 +1,509 @@
+/*     $NetBSD: cd9660_write.c,v 1.17 2013/10/19 17:16:37 christos Exp $       */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+
+#include "cd9660.h"
+#include "iso9660_rrip.h"
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: cd9660_write.c,v 1.17 2013/10/19 17:16:37 christos Exp $");
+#endif  /* !__lint */
+
+#include <util.h>
+
+static int cd9660_write_volume_descriptors(iso9660_disk *, FILE *);
+static int cd9660_write_path_table(iso9660_disk *, FILE *, off_t, int);
+static int cd9660_write_path_tables(iso9660_disk *, FILE *);
+static int cd9660_write_file(iso9660_disk *, FILE *, cd9660node *);
+static int cd9660_write_filedata(iso9660_disk *, FILE *, off_t,
+    const unsigned char *, int);
+#if 0
+static int cd9660_write_buffered(FILE *, off_t, int, const unsigned char *);
+#endif
+static void cd9660_write_rr(iso9660_disk *, FILE *, cd9660node *, off_t, off_t);
+
+/*
+ * Write the image
+ * Writes the entire image
+ * @param const char* The filename for the image
+ * @returns int 1 on success, 0 on failure
+ */
+int
+cd9660_write_image(iso9660_disk *diskStructure, const char* image)
+{
+       FILE *fd;
+       int status;
+       char buf[CD9660_SECTOR_SIZE];
+
+       if ((fd = fopen(image, "w+")) == NULL) {
+               err(EXIT_FAILURE, "%s: Can't open `%s' for writing", __func__,
+                   image);
+       }
+
+       if (diskStructure->verbose_level > 0)
+               printf("Writing image\n");
+
+       if (diskStructure->has_generic_bootimage) {
+               status = cd9660_copy_file(diskStructure, fd, 0,
+                   diskStructure->generic_bootimage);
+               if (status == 0) {
+                       warnx("%s: Error writing generic boot image",
+                           __func__);
+                       goto cleanup_bad_image;
+               }
+       }
+
+       /* Write the volume descriptors */
+       status = cd9660_write_volume_descriptors(diskStructure, fd);
+       if (status == 0) {
+               warnx("%s: Error writing volume descriptors to image",
+                   __func__);
+               goto cleanup_bad_image;
+       }
+
+       if (diskStructure->verbose_level > 0)
+               printf("Volume descriptors written\n");
+
+       /*
+        * Write the path tables: there are actually four, but right
+        * now we are only concearned with two.
+        */
+       status = cd9660_write_path_tables(diskStructure, fd);
+       if (status == 0) {
+               warnx("%s: Error writing path tables to image", __func__);
+               goto cleanup_bad_image;
+       }
+
+       if (diskStructure->verbose_level > 0)
+               printf("Path tables written\n");
+
+       /* Write the directories and files */
+       status = cd9660_write_file(diskStructure, fd, diskStructure->rootNode);
+       if (status == 0) {
+               warnx("%s: Error writing files to image", __func__);
+               goto cleanup_bad_image;
+       }
+
+       if (diskStructure->is_bootable) {
+               cd9660_write_boot(diskStructure, fd);
+       }
+
+       /* Write padding bits. This is temporary */
+       memset(buf, 0, CD9660_SECTOR_SIZE);
+       cd9660_write_filedata(diskStructure, fd,
+           diskStructure->totalSectors - 1, buf, 1);
+
+       if (diskStructure->verbose_level > 0)
+               printf("Files written\n");
+       fclose(fd);
+
+       if (diskStructure->verbose_level > 0)
+               printf("Image closed\n");
+       return 1;
+
+cleanup_bad_image:
+       fclose(fd);
+       if (!diskStructure->keep_bad_images)
+               unlink(image);
+       if (diskStructure->verbose_level > 0)
+               printf("Bad image cleaned up\n");
+       return 0;
+}
+
+static int
+cd9660_write_volume_descriptors(iso9660_disk *diskStructure, FILE *fd)
+{
+       volume_descriptor *vd_temp = diskStructure->firstVolumeDescriptor;
+       while (vd_temp != NULL) {
+               cd9660_write_filedata(diskStructure, fd, vd_temp->sector,
+                   vd_temp->volumeDescriptorData, 1);
+               vd_temp = vd_temp->next;
+       }
+       return 1;
+}
+
+/*
+ * Write out an individual path table
+ * Used just to keep redundant code to a minimum
+ * @param FILE *fd Valid file pointer
+ * @param int Sector to start writing path table to
+ * @param int Endian mode : BIG_ENDIAN or LITTLE_ENDIAN
+ * @returns int 1 on success, 0 on failure
+ */
+static int
+cd9660_write_path_table(iso9660_disk *diskStructure, FILE *fd, off_t sector,
+    int mode)
+{
+       int path_table_sectors = CD9660_BLOCKS(diskStructure->sectorSize,
+           diskStructure->pathTableLength);
+       unsigned char *buffer;
+       unsigned char *buffer_head;
+       int len;
+       path_table_entry temp_entry;
+       cd9660node *ptcur;
+
+       buffer = ecalloc(path_table_sectors, diskStructure->sectorSize);
+       buffer_head = buffer;
+
+       ptcur = diskStructure->rootNode;
+
+       while (ptcur != NULL) {
+               memset(&temp_entry, 0, sizeof(path_table_entry));
+               temp_entry.length[0] = ptcur->isoDirRecord->name_len[0];
+               temp_entry.extended_attribute_length[0] =
+                   ptcur->isoDirRecord->ext_attr_length[0];
+               memcpy(temp_entry.name, ptcur->isoDirRecord->name,
+                   temp_entry.length[0] + 1);
+
+               /* round up */
+               len = temp_entry.length[0] + 8 + (temp_entry.length[0] & 0x01);
+
+                /* todo: function pointers instead */
+               if (mode == LITTLE_ENDIAN) {
+                       cd9660_731(ptcur->fileDataSector,
+                           temp_entry.first_sector);
+                       cd9660_721((ptcur->parent == NULL ?
+                               1 : ptcur->parent->ptnumber),
+                           temp_entry.parent_number);
+               } else {
+                       cd9660_732(ptcur->fileDataSector,
+                           temp_entry.first_sector);
+                       cd9660_722((ptcur->parent == NULL ?
+                               1 : ptcur->parent->ptnumber),
+                           temp_entry.parent_number);
+               }
+
+
+               memcpy(buffer, &temp_entry, len);
+               buffer += len;
+
+               ptcur = ptcur->ptnext;
+       }
+
+       return cd9660_write_filedata(diskStructure, fd, sector, buffer_head,
+           path_table_sectors);
+}
+
+
+/*
+ * Write out the path tables to disk
+ * Each file descriptor should be pointed to by the PVD, so we know which
+ * sector to copy them to. One thing to watch out for: the only path tables
+ * stored are in the endian mode that the application is compiled for. So,
+ * the first thing to do is write out that path table, then to write the one
+ * in the other endian mode requires to convert the endianness of each entry
+ * in the table. The best way to do this would be to create a temporary
+ * path_table_entry structure, then for each path table entry, copy it to
+ * the temporary entry, translate, then copy that to disk.
+ *
+ * @param FILE* Valid file descriptor
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_write_path_tables(iso9660_disk *diskStructure, FILE *fd)
+{
+       if (cd9660_write_path_table(diskStructure, fd,
+           diskStructure->primaryLittleEndianTableSector, LITTLE_ENDIAN) == 0)
+               return 0;
+
+       if (cd9660_write_path_table(diskStructure, fd,
+           diskStructure->primaryBigEndianTableSector, BIG_ENDIAN) == 0)
+               return 0;
+
+       /* @TODO: handle remaining two path tables */
+       return 1;
+}
+
+/*
+ * Write a file to disk
+ * Writes a file, its directory record, and its data to disk
+ * This file is designed to be called RECURSIVELY, so initially call it
+ * with the root node. All of the records should store what sector the
+ * file goes in, so no computation should be  necessary.
+ *
+ * @param int fd Valid file descriptor
+ * @param struct cd9660node* writenode Pointer to the file to be written
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_write_file(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode)
+{
+       char *buf;
+       char *temp_file_name;
+       int ret;
+       off_t working_sector;
+       int cur_sector_offset;
+       iso_directory_record_cd9660 temp_record;
+       cd9660node *temp;
+       int rv = 0;
+
+       /* Todo : clean up variables */
+
+       temp_file_name = ecalloc(CD9660MAXPATH + 1, 1);
+       buf = emalloc(diskStructure->sectorSize);
+       if ((writenode->level != 0) &&
+           !(writenode->node->type & S_IFDIR)) {
+               fsinode *inode = writenode->node->inode;
+               /* Only attempt to write unwritten files that have length. */
+               if ((inode->flags & FI_WRITTEN) != 0) {
+                       INODE_WARNX(("%s: skipping written inode %d", __func__,
+                           (int)inode->st.st_ino));
+               } else if (writenode->fileDataLength > 0) {
+                       INODE_WARNX(("%s: writing inode %d blocks at %" PRIu32,
+                           __func__, (int)inode->st.st_ino, inode->ino));
+                       inode->flags |= FI_WRITTEN;
+                       cd9660_compute_full_filename(writenode,
+                           temp_file_name);
+                       ret = cd9660_copy_file(diskStructure, fd,
+                           writenode->fileDataSector, temp_file_name);
+                       if (ret == 0)
+                               goto out;
+               }
+       } else {
+               /*
+                * Here is a new revelation that ECMA didnt explain
+                * (at least not well).
+                * ALL . and .. records store the name "\0" and "\1"
+                * resepctively. So, for each directory, we have to
+                * make a new node.
+                *
+                * This is where it gets kinda messy, since we have to
+                * be careful of sector boundaries
+                */
+               cur_sector_offset = 0;
+               working_sector = writenode->fileDataSector;
+               if (fseeko(fd, working_sector * diskStructure->sectorSize,
+                   SEEK_SET) == -1)
+                       err(1, "fseeko");
+
+               /*
+                * Now loop over children, writing out their directory
+                * records - beware of sector boundaries
+                */
+               TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
+                       /*
+                        * Copy the temporary record and adjust its size
+                        * if necessary
+                        */
+                       memcpy(&temp_record, temp->isoDirRecord,
+                           sizeof(iso_directory_record_cd9660));
+
+                       temp_record.length[0] =
+                           cd9660_compute_record_size(diskStructure, temp);
+
+                       if (temp_record.length[0] + cur_sector_offset >=
+                           diskStructure->sectorSize) {
+                               cur_sector_offset = 0;
+                               working_sector++;
+
+                               /* Seek to the next sector. */
+                               if (fseeko(fd, working_sector *
+                                   diskStructure->sectorSize, SEEK_SET) == -1)
+                                       err(1, "fseeko");
+                       }
+                       /* Write out the basic ISO directory record */
+                       (void)fwrite(&temp_record, 1,
+                           temp->isoDirRecord->length[0], fd);
+                       if (diskStructure->rock_ridge_enabled) {
+                               cd9660_write_rr(diskStructure, fd, temp,
+                                   cur_sector_offset, working_sector);
+                       }
+                       if (fseeko(fd, working_sector *
+                           diskStructure->sectorSize + cur_sector_offset +
+                           temp_record.length[0] - temp->su_tail_size,
+                           SEEK_SET) == -1)
+                               err(1, "fseeko");
+                       if (temp->su_tail_size > 0)
+                               fwrite(temp->su_tail_data, 1,
+                                   temp->su_tail_size, fd);
+                       if (ferror(fd)) {
+                               warnx("%s: write error", __func__);
+                               goto out;
+                       }
+                       cur_sector_offset += temp_record.length[0];
+
+               }
+
+               /*
+                * Recurse on children.
+                */
+               TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
+                       if ((ret = cd9660_write_file(diskStructure, fd, temp))
+                           == 0)
+                               goto out;
+               }
+       }
+       rv = 1;
+out:
+       free(temp_file_name);
+       free(buf);
+       return rv;
+}
+
+/*
+ * Wrapper function to write a buffer (one sector) to disk.
+ * Seeks and writes the buffer.
+ * NOTE: You dont NEED to use this function, but it might make your
+ * life easier if you have to write things that align to a sector
+ * (such as volume descriptors).
+ *
+ * @param int fd Valid file descriptor
+ * @param int sector Sector number to write to
+ * @param const unsigned char* Buffer to write. This should be the
+ *                             size of a sector, and if only a portion
+ *                             is written, the rest should be set to 0.
+ */
+static int
+cd9660_write_filedata(iso9660_disk *diskStructure, FILE *fd, off_t sector,
+    const unsigned char *buf, int numsecs)
+{
+       off_t curpos;
+       size_t success;
+
+       curpos = ftello(fd);
+
+       if (fseeko(fd, sector * diskStructure->sectorSize, SEEK_SET) == -1)
+               err(1, "fseeko");
+
+       success = fwrite(buf, diskStructure->sectorSize * numsecs, 1, fd);
+
+       if (fseeko(fd, curpos, SEEK_SET) == -1)
+               err(1, "fseeko");
+
+       if (success == 1)
+               success = diskStructure->sectorSize * numsecs;
+       return success;
+}
+
+#if 0
+static int
+cd9660_write_buffered(FILE *fd, off_t offset, int buff_len,
+                     const unsigned char* buffer)
+{
+       static int working_sector = -1;
+       static char buf[CD9660_SECTOR_SIZE];
+
+       return 0;
+}
+#endif
+
+int
+cd9660_copy_file(iso9660_disk *diskStructure, FILE *fd, off_t start_sector,
+    const char *filename)
+{
+       FILE *rf;
+       int bytes_read;
+       off_t sector = start_sector;
+       int buf_size = diskStructure->sectorSize;
+       char *buf;
+
+       buf = emalloc(buf_size);
+       if ((rf = fopen(filename, "rb")) == NULL) {
+               warn("%s: cannot open %s", __func__, filename);
+               free(buf);
+               return 0;
+       }
+
+       if (diskStructure->verbose_level > 1)
+               printf("Writing file: %s\n",filename);
+
+       if (fseeko(fd, start_sector * diskStructure->sectorSize, SEEK_SET) == -1)
+               err(1, "fseeko");
+
+       while (!feof(rf)) {
+               bytes_read = fread(buf,1,buf_size,rf);
+               if (ferror(rf)) {
+                       warn("%s: fread", __func__);
+                       free(buf);
+                       (void)fclose(rf);
+                       return 0;
+               }
+
+               fwrite(buf,1,bytes_read,fd);
+               if (ferror(fd)) {
+                       warn("%s: fwrite", __func__);
+                       free(buf);
+                       (void)fclose(rf);
+                       return 0;
+               }
+               sector++;
+       }
+
+       fclose(rf);
+       free(buf);
+       return 1;
+}
+
+static void
+cd9660_write_rr(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode,
+    off_t offset, off_t sector)
+{
+       int in_ca = 0;
+       struct ISO_SUSP_ATTRIBUTES *myattr;
+
+       offset += writenode->isoDirRecord->length[0];
+       if (fseeko(fd, sector * diskStructure->sectorSize + offset, SEEK_SET) ==
+           -1)
+               err(1, "fseeko");
+       /* Offset now points at the end of the record */
+       TAILQ_FOREACH(myattr, &writenode->head, rr_ll) {
+               fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd);
+
+               if (!in_ca) {
+                       offset += CD9660_SUSP_ENTRY_SIZE(myattr);
+                       if (myattr->last_in_suf) {
+                               /*
+                                * Point the offset to the start of this
+                                * record's CE area
+                                */
+                               if (fseeko(fd, ((off_t)diskStructure->
+                                   susp_continuation_area_start_sector *
+                                   diskStructure->sectorSize)
+                                   + writenode->susp_entry_ce_start,
+                                   SEEK_SET) == -1)
+                                       err(1, "fseeko");
+                               in_ca = 1;
+                       }
+               }
+       }
+
+       /*
+        * If we had to go to the continuation area, head back to
+        * where we should be.
+        */
+       if (in_ca)
+               if (fseeko(fd, sector * diskStructure->sectorSize + offset,
+                   SEEK_SET) == -1)
+                       err(1, "fseeko");
+}
diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.c b/usr.sbin/makefs/cd9660/iso9660_rrip.c
new file mode 100644 (file)
index 0000000..9189be0
--- /dev/null
@@ -0,0 +1,838 @@
+/*     $NetBSD: iso9660_rrip.c,v 1.13 2013/07/30 16:02:23 reinoud Exp $        */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+/* This will hold all the function definitions
+ * defined in iso9660_rrip.h
+ */
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "iso9660_rrip.h"
+#include <sys/queue.h>
+#include <stdio.h>
+#include <util.h>
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: iso9660_rrip.c,v 1.13 2013/07/30 16:02:23 reinoud Exp $");
+#endif  /* !__lint */
+
+static void cd9660_rrip_initialize_inode(cd9660node *);
+static int cd9660_susp_handle_continuation(iso9660_disk *, cd9660node *);
+static int cd9660_susp_handle_continuation_common(iso9660_disk *, cd9660node *,
+    int);
+
+int
+cd9660_susp_initialize(iso9660_disk *diskStructure, cd9660node *node,
+    cd9660node *parent, cd9660node *grandparent)
+{
+       cd9660node *cn;
+       int r;
+
+       /* Make sure the node is not NULL. If it is, there are major problems */
+       assert(node != NULL);
+
+       if (!(node->type & CD9660_TYPE_DOT) &&
+           !(node->type & CD9660_TYPE_DOTDOT))
+               TAILQ_INIT(&(node->head));
+       if (node->dot_record != 0)
+               TAILQ_INIT(&(node->dot_record->head));
+       if (node->dot_dot_record != 0)
+               TAILQ_INIT(&(node->dot_dot_record->head));
+
+        /* SUSP specific entries here */
+       if ((r = cd9660_susp_initialize_node(diskStructure, node)) < 0)
+               return r;
+
+       /* currently called cd9660node_rrip_init_links */
+       r = cd9660_rrip_initialize_node(diskStructure, node, parent, grandparent);
+       if (r < 0)
+               return r;
+
+       /*
+        * See if we need a CE record, and set all of the
+        * associated counters.
+        *
+        * This should be called after all extensions. After
+        * this is called, no new records should be added.
+        */
+       if ((r = cd9660_susp_handle_continuation(diskStructure, node)) < 0)
+               return r;
+
+       /* Recurse on children. */
+       TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
+               if ((r = cd9660_susp_initialize(diskStructure, cn, node, parent)) < 0)
+                       return 0;
+       }
+       return 1;
+}
+
+int
+cd9660_susp_finalize(iso9660_disk *diskStructure, cd9660node *node)
+{
+       cd9660node *temp;
+       int r;
+
+       assert(node != NULL);
+
+       if (node == diskStructure->rootNode)
+               diskStructure->susp_continuation_area_current_free = 0;
+
+       if ((r = cd9660_susp_finalize_node(diskStructure, node)) < 0)
+               return r;
+       if ((r = cd9660_rrip_finalize_node(diskStructure, node)) < 0)
+               return r;
+
+       TAILQ_FOREACH(temp, &node->cn_children, cn_next_child) {
+               if ((r = cd9660_susp_finalize(diskStructure, temp)) < 0)
+                       return r;
+       }
+       return 1;
+}
+
+/*
+ * If we really wanted to speed things up, we could have some sort of
+ * lookup table on the SUSP entry type that calls a functor. Or, we could
+ * combine the functions. These functions are kept separate to allow
+ * easier addition of other extensions.
+
+ * For the sake of simplicity and clarity, we won't be doing that for now.
+ */
+
+/*
+ * SUSP needs to update the following types:
+ * CE (continuation area)
+ */
+int
+cd9660_susp_finalize_node(iso9660_disk *diskStructure, cd9660node *node)
+{
+       struct ISO_SUSP_ATTRIBUTES *t;
+
+       /* Handle CE counters */
+       if (node->susp_entry_ce_length > 0) {
+               node->susp_entry_ce_start =
+                   diskStructure->susp_continuation_area_current_free;
+               diskStructure->susp_continuation_area_current_free +=
+                   node->susp_entry_ce_length;
+       }
+
+       TAILQ_FOREACH(t, &node->head, rr_ll) {
+               if (t->susp_type != SUSP_TYPE_SUSP ||
+                   t->entry_type != SUSP_ENTRY_SUSP_CE)
+                       continue;
+               cd9660_bothendian_dword(
+                       diskStructure->
+                         susp_continuation_area_start_sector,
+                       t->attr.su_entry.CE.ca_sector);
+
+               cd9660_bothendian_dword(
+                       diskStructure->
+                         susp_continuation_area_start_sector,
+                       t->attr.su_entry.CE.ca_sector);
+               cd9660_bothendian_dword(node->susp_entry_ce_start,
+                       t->attr.su_entry.CE.offset);
+               cd9660_bothendian_dword(node->susp_entry_ce_length,
+                       t->attr.su_entry.CE.length);
+       }
+       return 0;
+}
+
+int
+cd9660_rrip_finalize_node(iso9660_disk *diskStructure __unused,
+    cd9660node *node)
+{
+       struct ISO_SUSP_ATTRIBUTES *t;
+
+       TAILQ_FOREACH(t, &node->head, rr_ll) {
+               if (t->susp_type != SUSP_TYPE_RRIP)
+                       continue;
+               switch (t->entry_type) {
+               case SUSP_ENTRY_RRIP_CL:
+                       /* Look at rr_relocated*/
+                       if (node->rr_relocated == NULL)
+                               return -1;
+                       cd9660_bothendian_dword(
+                               node->rr_relocated->fileDataSector,
+                               (unsigned char *)
+                                   t->attr.rr_entry.CL.dir_loc);
+                       break;
+               case SUSP_ENTRY_RRIP_PL:
+                       /* Look at rr_real_parent */
+                       if (node->parent == NULL ||
+                           node->parent->rr_real_parent == NULL)
+                               return -1;
+                       cd9660_bothendian_dword(
+                               node->parent->rr_real_parent->fileDataSector,
+                               (unsigned char *)
+                                   t->attr.rr_entry.PL.dir_loc);
+                       break;
+               }
+       }
+       return 0;
+}
+
+static int
+cd9660_susp_handle_continuation_common(iso9660_disk *diskStructure,
+    cd9660node *node, int space)
+{
+       int ca_used, susp_used, susp_used_pre_ce, working;
+       struct ISO_SUSP_ATTRIBUTES *temp, *pre_ce, *last, *CE, *ST;
+
+       pre_ce = last = NULL;
+       working = 254 - space;
+       if (node->su_tail_size > 0)
+               /* Allow 4 bytes for "ST" record. */
+               working -= node->su_tail_size + 4;
+       /* printf("There are %i bytes to work with\n",working); */
+
+       susp_used_pre_ce = susp_used = 0;
+       ca_used = 0;
+       TAILQ_FOREACH(temp, &node->head, rr_ll) {
+               if (working < 0)
+                       break;
+               /*
+                * printf("SUSP Entry found, length is %i\n",
+                * CD9660_SUSP_ENTRY_SIZE(temp));
+                */
+               working -= CD9660_SUSP_ENTRY_SIZE(temp);
+               if (working >= 0) {
+                       last = temp;
+                       susp_used += CD9660_SUSP_ENTRY_SIZE(temp);
+               }
+               if (working >= 28) {
+                       /*
+                        * Remember the last entry after which we
+                        * could insert a "CE" entry.
+                        */
+                       pre_ce = last;
+                       susp_used_pre_ce = susp_used;
+               }
+       }
+
+       /* A CE entry is needed */
+       if (working <= 0) {
+               CE = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+                       SUSP_ENTRY_SUSP_CE, "CE", SUSP_LOC_ENTRY);
+               cd9660_susp_ce(CE, node);
+               /* This will automatically insert at the appropriate location */
+               if (pre_ce != NULL)
+                       TAILQ_INSERT_AFTER(&node->head, pre_ce, CE, rr_ll);
+               else
+                       TAILQ_INSERT_HEAD(&node->head, CE, rr_ll);
+               last = CE;
+               susp_used = susp_used_pre_ce + 28;
+               /* Count how much CA data is necessary */
+               for (temp = TAILQ_NEXT(last, rr_ll); temp != NULL;
+                    temp = TAILQ_NEXT(temp, rr_ll)) {
+                       ca_used += CD9660_SUSP_ENTRY_SIZE(temp);
+               }
+       }
+
+       /* An ST entry is needed */
+       if (node->su_tail_size > 0) {
+               ST = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+                   SUSP_ENTRY_SUSP_ST, "ST", SUSP_LOC_ENTRY);
+               cd9660_susp_st(ST, node);
+               if (last != NULL)
+                       TAILQ_INSERT_AFTER(&node->head, last, ST, rr_ll);
+               else
+                       TAILQ_INSERT_HEAD(&node->head, ST, rr_ll);
+               last = ST;
+               susp_used += 4;
+       }
+       if (last != NULL)
+               last->last_in_suf = 1;
+
+       node->susp_entry_size = susp_used;
+       node->susp_entry_ce_length = ca_used;
+
+       diskStructure->susp_continuation_area_size += ca_used;
+       return 1;
+}
+
+/* See if a continuation entry is needed for each of the different types */
+static int
+cd9660_susp_handle_continuation(iso9660_disk *diskStructure, cd9660node *node)
+{
+       assert (node != NULL);
+
+       /* Entry */
+       if (cd9660_susp_handle_continuation_common(diskStructure,
+               node,(int)(node->isoDirRecord->length[0])) < 0)
+               return 0;
+
+       return 1;
+}
+
+int
+cd9660_susp_initialize_node(iso9660_disk *diskStructure, cd9660node *node)
+{
+       struct ISO_SUSP_ATTRIBUTES *temp;
+
+       /*
+        * Requirements/notes:
+        * CE: is added for us where needed
+        * ST: not sure if it is even required, but if so, should be
+        *     handled by the CE code
+        * PD: isnt needed (though might be added for testing)
+        * SP: is stored ONLY on the . record of the root directory
+        * ES: not sure
+        */
+
+       /* Check for root directory, add SP and ER if needed. */
+       if (node->type & CD9660_TYPE_DOT) {
+               if (node->parent == diskStructure->rootNode) {
+                       temp = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+                               SUSP_ENTRY_SUSP_SP, "SP", SUSP_LOC_DOT);
+                       cd9660_susp_sp(temp, node);
+
+                       /* Should be first entry. */
+                       TAILQ_INSERT_HEAD(&node->head, temp, rr_ll);
+               }
+       }
+       return 1;
+}
+
+static void
+cd9660_rrip_initialize_inode(cd9660node *node)
+{
+       struct ISO_SUSP_ATTRIBUTES *attr;
+
+       /*
+        * Inode dependent values - this may change,
+        * but for now virtual files and directories do
+        * not have an inode structure
+        */
+
+       if ((node->node != NULL) && (node->node->inode != NULL)) {
+               /* PX - POSIX attributes */
+               attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+                       SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
+               cd9660node_rrip_px(attr, node->node);
+
+               TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
+
+               /* TF - timestamp */
+               attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+                       SUSP_ENTRY_RRIP_TF, "TF", SUSP_LOC_ENTRY);
+               cd9660node_rrip_tf(attr, node->node);
+               TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
+
+               /* SL - Symbolic link */
+               /* ?????????? Dan - why is this here? */
+               if (TAILQ_EMPTY(&node->cn_children) &&
+                   node->node->inode != NULL &&
+                   S_ISLNK(node->node->inode->st.st_mode))
+                       cd9660_createSL(node);
+
+               /* PN - device number */
+               if (node->node->inode != NULL &&
+                   ((S_ISCHR(node->node->inode->st.st_mode) ||
+                    S_ISBLK(node->node->inode->st.st_mode)))) {
+                       attr =
+                           cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+                               SUSP_ENTRY_RRIP_PN, "PN",
+                               SUSP_LOC_ENTRY);
+                       cd9660node_rrip_pn(attr, node->node);
+                       TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
+               }
+       }
+}
+
+int
+cd9660_rrip_initialize_node(iso9660_disk *diskStructure, cd9660node *node,
+    cd9660node *parent, cd9660node *grandparent)
+{
+       struct ISO_SUSP_ATTRIBUTES *current = NULL;
+
+       assert(node != NULL);
+
+       if (node->type & CD9660_TYPE_DOT) {
+               /*
+                * Handle ER - should be the only entry to appear on
+                * a "." record
+                */
+               if (node->parent == diskStructure->rootNode) {
+                       cd9660_susp_ER(node, 1, SUSP_RRIP_ER_EXT_ID,
+                               SUSP_RRIP_ER_EXT_DES, SUSP_RRIP_ER_EXT_SRC);
+               }
+               if (parent != NULL && parent->node != NULL &&
+                   parent->node->inode != NULL) {
+                       /* PX - POSIX attributes */
+                       current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+                               SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
+                       cd9660node_rrip_px(current, parent->node);
+                       TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+               }
+       } else if (node->type & CD9660_TYPE_DOTDOT) {
+               if (grandparent != NULL && grandparent->node != NULL &&
+                   grandparent->node->inode != NULL) {
+                       /* PX - POSIX attributes */
+                       current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+                               SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
+                       cd9660node_rrip_px(current, grandparent->node);
+                       TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+               }
+               /* Handle PL */
+               if (parent != NULL && parent->rr_real_parent != NULL) {
+                       current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+                           SUSP_ENTRY_RRIP_PL, "PL", SUSP_LOC_DOTDOT);
+                       cd9660_rrip_PL(current,node);
+                       TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+               }
+       } else {
+               cd9660_rrip_initialize_inode(node);
+
+               /*
+                * Not every node needs a NM set - only if the name is
+                * actually different. IE: If a file is TEST -> TEST,
+                * no NM. test -> TEST, need a NM
+                *
+                * The rr_moved_dir needs to be assigned a NM record as well.
+                */
+               if (node == diskStructure->rr_moved_dir) {
+                       cd9660_rrip_add_NM(node, RRIP_DEFAULT_MOVE_DIR_NAME);
+               }
+               else if ((node->node != NULL) &&
+                       ((strlen(node->node->name) !=
+                           (uint8_t)node->isoDirRecord->name_len[0]) ||
+                       (memcmp(node->node->name,node->isoDirRecord->name,
+                               (uint8_t)node->isoDirRecord->name_len[0]) != 0))) {
+                       cd9660_rrip_NM(node);
+               }
+
+
+
+               /* Rock ridge directory relocation code here. */
+
+               /* First handle the CL for the placeholder file. */
+               if (node->rr_relocated != NULL) {
+                       current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+                               SUSP_ENTRY_RRIP_CL, "CL", SUSP_LOC_ENTRY);
+                       cd9660_rrip_CL(current, node);
+                       TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+               }
+
+               /* Handle RE*/
+               if (node->rr_real_parent != NULL) {
+                       current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+                               SUSP_ENTRY_RRIP_RE, "RE", SUSP_LOC_ENTRY);
+                       cd9660_rrip_RE(current,node);
+                       TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+               }
+       }
+       return 1;
+}
+
+struct ISO_SUSP_ATTRIBUTES*
+cd9660node_susp_create_node(int susp_type, int entry_type, const char *type_id,
+                           int write_loc)
+{
+       struct ISO_SUSP_ATTRIBUTES* temp;
+
+       temp = emalloc(sizeof(*temp));
+       temp->susp_type = susp_type;
+       temp->entry_type = entry_type;
+       temp->last_in_suf = 0;
+       /* Phase this out */
+       temp->type_of[0] = type_id[0];
+       temp->type_of[1] = type_id[1];
+       temp->write_location = write_loc;
+
+       /*
+        * Since the first four bytes is common, lets go ahead and
+        * set the type identifier, since we are passing that to this
+        * function anyhow.
+        */
+       temp->attr.su_entry.SP.h.type[0] = type_id[0];
+       temp->attr.su_entry.SP.h.type[1] = type_id[1];
+       return temp;
+}
+
+int
+cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES* p, cd9660node *node __unused)
+{
+       p->attr.rr_entry.PL.h.length[0] = 12;
+       p->attr.rr_entry.PL.h.version[0] = 1;
+       return 1;
+}
+
+int
+cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused)
+{
+       p->attr.rr_entry.CL.h.length[0] = 12;
+       p->attr.rr_entry.CL.h.version[0] = 1;
+       return 1;
+}
+
+int
+cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused)
+{
+       p->attr.rr_entry.RE.h.length[0] = 4;
+       p->attr.rr_entry.RE.h.version[0] = 1;
+       return 1;
+}
+
+void
+cd9660_createSL(cd9660node *node)
+{
+       struct ISO_SUSP_ATTRIBUTES* current;
+       int path_count, dir_count, done, i, j, dir_copied;
+       char temp_cr[255];
+       char temp_sl[255]; /* used in copying continuation entry*/
+       char* sl_ptr;
+
+       sl_ptr = node->node->symlink;
+
+       done = 0;
+       path_count = 0;
+       dir_count = 0;
+       dir_copied = 0;
+       current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+           SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY);
+
+       current->attr.rr_entry.SL.h.version[0] = 1;
+       current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE;
+
+       if (*sl_ptr == '/') {
+               temp_cr[0] = SL_FLAGS_ROOT;
+               temp_cr[1] = 0;
+               memcpy(current->attr.rr_entry.SL.component + path_count,
+                   temp_cr, 2);
+               path_count += 2;
+               sl_ptr++;
+       }
+
+       for (i = 0; i < (dir_count + 2); i++)
+               temp_cr[i] = '\0';
+
+       while (!done) {
+               while ((*sl_ptr != '/') && (*sl_ptr != '\0')) {
+                       dir_copied = 1;
+                       if (*sl_ptr == '.') {
+                               if ((*(sl_ptr + 1) == '/') || (*(sl_ptr + 1)
+                                    == '\0')) {
+                                       temp_cr[0] = SL_FLAGS_CURRENT;
+                                       sl_ptr++;
+                               } else if(*(sl_ptr + 1) == '.') {
+                                       if ((*(sl_ptr + 2) == '/') ||
+                                           (*(sl_ptr + 2) == '\0')) {
+                                               temp_cr[0] = SL_FLAGS_PARENT;
+                                               sl_ptr += 2;
+                                       }
+                               } else {
+                                       temp_cr[dir_count+2] = *sl_ptr;
+                                       sl_ptr++;
+                                       dir_count++;
+                               }
+                       } else {
+                               temp_cr[dir_count + 2] = *sl_ptr;
+                               sl_ptr++;
+                               dir_count++;
+                       }
+               }
+
+               if ((path_count + dir_count) >= 249) {
+                       current->attr.rr_entry.SL.flags[0] |= SL_FLAGS_CONTINUE;
+
+                       j = 0;
+
+                       if (path_count <= 249) {
+                               while(j != (249 - path_count)) {
+                                       temp_sl[j] = temp_cr[j];
+                                       j++;
+                               }
+                               temp_sl[0] = SL_FLAGS_CONTINUE;
+                               temp_sl[1] = j - 2;
+                               memcpy(
+                                   current->attr.rr_entry.SL.component +
+                                       path_count,
+                                   temp_sl, j);
+                       }
+
+                       path_count += j;
+                       current->attr.rr_entry.SL.h.length[0] = path_count + 5;
+                       TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+                       current= cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+                              SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY);
+                       current->attr.rr_entry.SL.h.version[0] = 1;
+                       current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE;
+
+                       path_count = 0;
+
+                       if (dir_count > 2) {
+                               while (j != dir_count + 2) {
+                                       current->attr.rr_entry.SL.component[
+                                           path_count + 2] = temp_cr[j];
+                                       j++;
+                                       path_count++;
+                               }
+                               current->attr.rr_entry.SL.component[1]
+                                   = path_count;
+                               path_count+= 2;
+                       } else {
+                               while(j != dir_count) {
+                                       current->attr.rr_entry.SL.component[
+                                           path_count+2] = temp_cr[j];
+                                       j++;
+                                       path_count++;
+                               }
+                       }
+               } else {
+                       if (dir_copied == 1) {
+                               temp_cr[1] = dir_count;
+                               memcpy(current->attr.rr_entry.SL.component +
+                                       path_count,
+                                   temp_cr, dir_count + 2);
+                               path_count += dir_count + 2;
+                       }
+               }
+
+               if (*sl_ptr == '\0') {
+                       done = 1;
+                       current->attr.rr_entry.SL.h.length[0] = path_count + 5;
+                       TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+               } else {
+                       sl_ptr++;
+                       dir_count = 0;
+                       dir_copied = 0;
+                       for(i = 0; i < 255; i++) {
+                               temp_cr[i] = '\0';
+                       }
+               }
+       }
+}
+
+int
+cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *v, fsnode *pxinfo)
+{
+       v->attr.rr_entry.PX.h.length[0] = 36;
+       v->attr.rr_entry.PX.h.version[0] = 1;
+       cd9660_bothendian_dword(pxinfo->inode->st.st_mode,
+           v->attr.rr_entry.PX.mode);
+       cd9660_bothendian_dword(pxinfo->inode->st.st_nlink,
+           v->attr.rr_entry.PX.links);
+       cd9660_bothendian_dword(pxinfo->inode->st.st_uid,
+           v->attr.rr_entry.PX.uid);
+       cd9660_bothendian_dword(pxinfo->inode->st.st_gid,
+           v->attr.rr_entry.PX.gid);
+
+       /* Ignoring the serial number for now */
+       return 1;
+}
+
+int
+cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *pn_field, fsnode *fnode)
+{
+       pn_field->attr.rr_entry.PN.h.length[0] = 20;
+       pn_field->attr.rr_entry.PN.h.version[0] = 1;
+
+       if (sizeof (fnode->inode->st.st_dev) > 32)
+               cd9660_bothendian_dword((uint64_t)fnode->inode->st.st_dev >> 32,
+                   pn_field->attr.rr_entry.PN.high);
+       else
+               cd9660_bothendian_dword(0, pn_field->attr.rr_entry.PN.high);
+
+       cd9660_bothendian_dword(fnode->inode->st.st_dev & 0xffffffff,
+               pn_field->attr.rr_entry.PN.low);
+       return 1;
+}
+
+#if 0
+int
+cd9660node_rrip_nm(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *file_node)
+{
+       int nm_length = strlen(file_node->isoDirRecord->name) + 5;
+        p->attr.rr_entry.NM.h.type[0] = 'N';
+       p->attr.rr_entry.NM.h.type[1] = 'M';
+       sprintf(p->attr.rr_entry.NM.altname, "%s", file_node->isoDirRecord->name);
+       p->attr.rr_entry.NM.h.length[0] = (unsigned char)nm_length;
+       p->attr.rr_entry.NM.h.version[0] = (unsigned char)1;
+       p->attr.rr_entry.NM.flags[0] = (unsigned char) NM_PARENT;
+       return 1;
+}
+#endif
+
+int
+cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *p, fsnode *_node)
+{
+       p->attr.rr_entry.TF.flags[0] = TF_MODIFY | TF_ACCESS | TF_ATTRIBUTES;
+       p->attr.rr_entry.TF.h.length[0] = 5;
+       p->attr.rr_entry.TF.h.version[0] = 1;
+
+       /*
+        * Need to add creation time, backup time,
+        * expiration time, and effective time.
+        */
+
+       cd9660_time_915(p->attr.rr_entry.TF.timestamp,
+               _node->inode->st.st_atime);
+       p->attr.rr_entry.TF.h.length[0] += 7;
+
+       cd9660_time_915(p->attr.rr_entry.TF.timestamp + 7,
+               _node->inode->st.st_mtime);
+       p->attr.rr_entry.TF.h.length[0] += 7;
+
+       cd9660_time_915(p->attr.rr_entry.TF.timestamp + 14,
+               _node->inode->st.st_ctime);
+       p->attr.rr_entry.TF.h.length[0] += 7;
+       return 1;
+}
+
+int
+cd9660_susp_sp(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused)
+{
+       p->attr.su_entry.SP.h.length[0] = 7;
+       p->attr.su_entry.SP.h.version[0] = 1;
+       p->attr.su_entry.SP.check[0] = 0xBE;
+       p->attr.su_entry.SP.check[1] = 0xEF;
+       p->attr.su_entry.SP.len_skp[0] = 0;
+       return 1;
+}
+
+int
+cd9660_susp_st(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *stinfo __unused)
+{
+       p->attr.su_entry.ST.h.type[0] = 'S';
+       p->attr.su_entry.ST.h.type[1] = 'T';
+       p->attr.su_entry.ST.h.length[0] = 4;
+       p->attr.su_entry.ST.h.version[0] = 1;
+       return 1;
+}
+
+int
+cd9660_susp_ce(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused)
+{
+       p->attr.su_entry.CE.h.length[0] = 28;
+       p->attr.su_entry.CE.h.version[0] = 1;
+       /* Other attributes dont matter right now, will be updated later */
+       return 1;
+}
+
+int
+cd9660_susp_pd(struct ISO_SUSP_ATTRIBUTES *p __unused, int length __unused)
+{
+       return 1;
+}
+
+void
+cd9660_rrip_add_NM(cd9660node *node, const char *name)
+{
+       int working,len;
+       const char *p;
+       struct ISO_SUSP_ATTRIBUTES *r;
+
+       /*
+        * Each NM record has 254 byes to work with. This means that
+        * the name data itself only has 249 bytes to work with. So, a
+        * name with 251 characters would require two nm records.
+        */
+       p = name;
+       working = 1;
+       while (working) {
+               r = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+                   SUSP_ENTRY_RRIP_NM, "NM", SUSP_LOC_ENTRY);
+               r->attr.rr_entry.NM.h.version[0] = 1;
+               r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_NONE;
+               len = strlen(p);
+
+               if (len > 249) {
+                       len = 249;
+                       r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_CONTINUE;
+               } else {
+                       working = 0;
+               }
+               memcpy(r->attr.rr_entry.NM.altname, p, len);
+               r->attr.rr_entry.NM.h.length[0] = 5 + len;
+
+               TAILQ_INSERT_TAIL(&node->head, r, rr_ll);
+
+               p += len;
+       }
+}
+
+void
+cd9660_rrip_NM(cd9660node *node)
+{
+       cd9660_rrip_add_NM(node, node->node->name);
+}
+
+struct ISO_SUSP_ATTRIBUTES*
+cd9660_susp_ER(cd9660node *node,
+              u_char ext_version, const char* ext_id, const char* ext_des,
+              const char* ext_src)
+{
+       int l;
+       struct ISO_SUSP_ATTRIBUTES *r;
+
+       r = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+                       SUSP_ENTRY_SUSP_ER, "ER", SUSP_LOC_DOT);
+
+       /* Fixed data is 8 bytes */
+       r->attr.su_entry.ER.h.length[0] = 8;
+       r->attr.su_entry.ER.h.version[0] = 1;
+
+       r->attr.su_entry.ER.len_id[0] = (u_char)strlen(ext_id);
+       r->attr.su_entry.ER.len_des[0] = (u_char)strlen(ext_des);
+       r->attr.su_entry.ER.len_src[0] = (u_char)strlen(ext_src);
+
+       l = r->attr.su_entry.ER.len_id[0] +
+               r->attr.su_entry.ER.len_src[0] +
+               r->attr.su_entry.ER.len_des[0];
+
+       /* Everything must fit. */
+       assert(l + r->attr.su_entry.ER.h.length[0] <= 254);
+
+       r->attr.su_entry.ER.h.length[0] += (u_char)l;
+
+
+       r->attr.su_entry.ER.ext_ver[0] = ext_version;
+       memcpy(r->attr.su_entry.ER.ext_data, ext_id,
+               (int)r->attr.su_entry.ER.len_id[0]);
+       l = (int) r->attr.su_entry.ER.len_id[0];
+       memcpy(r->attr.su_entry.ER.ext_data + l,ext_des,
+               (int)r->attr.su_entry.ER.len_des[0]);
+
+       l += (int)r->attr.su_entry.ER.len_des[0];
+       memcpy(r->attr.su_entry.ER.ext_data + l,ext_src,
+               (int)r->attr.su_entry.ER.len_src[0]);
+
+       TAILQ_INSERT_TAIL(&node->head, r, rr_ll);
+       return r;
+}
+
+struct ISO_SUSP_ATTRIBUTES*
+cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES *last __unused, cd9660node *node __unused)
+{
+       return NULL;
+}
diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.h b/usr.sbin/makefs/cd9660/iso9660_rrip.h
new file mode 100644 (file)
index 0000000..1f54ae8
--- /dev/null
@@ -0,0 +1,290 @@
+/*     $NetBSD: iso9660_rrip.h,v 1.6 2013/01/28 21:03:28 christos Exp $        */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+#ifndef __ISO9660_RRIP_H__
+#define __ISO9660_RRIP_H__
+
+/*
+ * This will hold all the functions needed to
+ * write an ISO 9660 image with Rock Ridge Extensions
+ */
+
+/* For writing must use ISO_RRIP_EXTREF structure */
+
+#include "makefs.h"
+#include <cd9660_rrip.h>
+#include "cd9660.h"
+#include <sys/queue.h>
+
+#define         PX_LENGTH         0x2C
+#define         PN_LENGTH         0x14
+#define         TF_CREATION       0x00
+#define         TF_MODIFY         0x01
+#define         TF_ACCESS         0x02
+#define         TF_ATTRIBUTES     0x04
+#define         TF_BACKUP         0x08
+#define         TF_EXPIRATION     0x10
+#define         TF_EFFECTIVE      0x20
+#define         TF_LONGFORM       0x40
+#define  NM_CONTINUE      0x80
+#define         NM_CURRENT        0x100
+#define         NM_PARENT         0x200
+
+
+#define         SUSP_LOC_ENTRY    0x01
+#define         SUSP_LOC_DOT      0x02
+#define         SUSP_LOC_DOTDOT   0x04
+
+#define SUSP_TYPE_SUSP         1
+#define SUSP_TYPE_RRIP         2
+
+#define SUSP_ENTRY_SUSP_CE     1
+#define SUSP_ENTRY_SUSP_PD     2
+#define SUSP_ENTRY_SUSP_SP     3
+#define SUSP_ENTRY_SUSP_ST     4
+#define SUSP_ENTRY_SUSP_ER     5
+#define SUSP_ENTRY_SUSP_ES     6
+
+#define SUSP_ENTRY_RRIP_PX     1
+#define SUSP_ENTRY_RRIP_PN     2
+#define SUSP_ENTRY_RRIP_SL     3
+#define SUSP_ENTRY_RRIP_NM     4
+#define SUSP_ENTRY_RRIP_CL     5
+#define SUSP_ENTRY_RRIP_PL     6
+#define SUSP_ENTRY_RRIP_RE     7
+#define SUSP_ENTRY_RRIP_TF     8
+#define SUSP_ENTRY_RRIP_SF     9
+
+#define SUSP_RRIP_ER_EXT_ID "IEEE_P1282"
+#define SUSP_RRIP_ER_EXT_DES "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS."
+#define SUSP_RRIP_ER_EXT_SRC "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION."
+
+#define SL_FLAGS_NONE          0
+#define SL_FLAGS_CONTINUE       1
+#define SL_FLAGS_CURRENT        2
+#define SL_FLAGS_PARENT                4
+#define SL_FLAGS_ROOT          8
+
+typedef struct {
+       ISO_SUSP_HEADER          h;
+       u_char mode             [ISODCL(5,12)];
+       u_char links            [ISODCL(13,20)];
+       u_char uid              [ISODCL(21,28)];
+       u_char gid              [ISODCL(29,36)];
+       u_char serial           [ISODCL(37,44)];/* Not used */
+} ISO_RRIP_PX;
+
+typedef struct {
+       ISO_SUSP_HEADER          h;
+       u_char high             [ISODCL(5,12)];
+       u_char low              [ISODCL(13,20)];
+} ISO_RRIP_PN;
+
+typedef struct {
+       ISO_SUSP_HEADER          h;
+       u_char flags             [ISODCL ( 4, 4)];
+       u_char component         [ISODCL ( 4, 256)];
+       u_int  nBytes;
+} ISO_RRIP_SL;
+
+typedef struct {
+       ISO_SUSP_HEADER          h;
+       u_char flags             [ISODCL ( 4, 4)];
+       u_char timestamp         [ISODCL ( 5, 256)];
+} ISO_RRIP_TF;
+
+#define RRIP_NM_FLAGS_NONE 0x00
+#define RRIP_NM_FLAGS_CONTINUE 0x01
+#define RRIP_NM_FLAGS_CURRENT 0x02
+#define RRIP_NM_FLAGS_PARENT 0x04
+
+typedef struct {
+       ISO_SUSP_HEADER          h;
+       u_char flags             [ISODCL ( 4, 4)];
+       u_char altname           [ISODCL ( 4, 256)];
+} ISO_RRIP_NM;
+
+/* Note that this is the same structure as cd9660_rrip.h : ISO_RRIP_CONT */
+typedef struct {
+       ISO_SUSP_HEADER          h;
+       u_char ca_sector         [ISODCL ( 5, 12)];
+       u_char offset            [ISODCL ( 13, 20)];
+       u_char length            [ISODCL ( 21, 28)];
+} ISO_SUSP_CE;
+
+typedef struct {
+       ISO_SUSP_HEADER          h;
+       u_char padding_area      [ISODCL ( 4, 256)];
+} ISO_SUSP_PD;
+
+typedef struct {
+       ISO_SUSP_HEADER          h;
+       u_char check             [ISODCL ( 4, 5)];
+       u_char len_skp           [ISODCL ( 6, 6)];
+} ISO_SUSP_SP;
+
+typedef struct {
+       ISO_SUSP_HEADER          h;
+} ISO_SUSP_ST;
+
+typedef struct {
+       ISO_SUSP_HEADER          h;
+       u_char len_id            [ISODCL ( 4, 4)];
+       u_char len_des           [ISODCL ( 5, 5)];
+       u_char len_src           [ISODCL ( 6, 6)];
+       u_char ext_ver           [ISODCL ( 7, 7)];
+       u_char ext_data          [ISODCL (8,256)];
+/*     u_char ext_id            [ISODCL ( 8, 256)];
+       u_char ext_des           [ISODCL ( 257, 513)];
+       u_char ext_src           [ISODCL ( 514, 770)];*/
+} ISO_SUSP_ER;
+
+typedef struct {
+       ISO_SUSP_HEADER          h;
+       u_char ext_seq           [ISODCL ( 4, 4)];
+} ISO_SUSP_ES;
+
+typedef union {
+       ISO_RRIP_PX                     PX;
+       ISO_RRIP_PN                     PN;
+       ISO_RRIP_SL                     SL;
+       ISO_RRIP_NM                     NM;
+       ISO_RRIP_CLINK                  CL;
+       ISO_RRIP_PLINK                  PL;
+       ISO_RRIP_RELDIR                 RE;
+       ISO_RRIP_TF                     TF;
+} rrip_entry;
+
+typedef union {
+       ISO_SUSP_CE                     CE;
+       ISO_SUSP_PD                     PD;
+       ISO_SUSP_SP                     SP;
+       ISO_SUSP_ST                     ST;
+       ISO_SUSP_ER                     ER;
+       ISO_SUSP_ES                     ES;
+} susp_entry;
+
+typedef union {
+       susp_entry                su_entry;
+       rrip_entry                rr_entry;
+} SUSP_ENTRIES;
+
+struct ISO_SUSP_ATTRIBUTES {
+       SUSP_ENTRIES attr;
+       int type;
+       char type_of[2];
+       char last_in_suf;       /* last entry in the System Use Field? */
+       /* Dan's addons - will merge later. This allows use of a switch */
+       char susp_type;         /* SUSP or RRIP */
+       char entry_type;        /* Record type */
+       char write_location;
+       TAILQ_ENTRY(ISO_SUSP_ATTRIBUTES) rr_ll;
+};
+
+#define CD9660_SUSP_ENTRY_SIZE(entry)\
+       ((int) ((entry)->attr.su_entry.SP.h.length[0]))
+
+/* Recursive function - move later to func pointer code*/
+int cd9660_susp_finalize(iso9660_disk *, cd9660node *);
+
+/* These two operate on single nodes */
+int cd9660_susp_finalize_node(iso9660_disk *, cd9660node *);
+int cd9660_rrip_finalize_node(iso9660_disk *, cd9660node *);
+
+/* POSIX File attribute */
+int cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Device number */
+int cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Symbolic link */
+int cd9660node_rrip_SL(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Alternate Name function */
+void cd9660_rrip_NM(cd9660node *);
+void cd9660_rrip_add_NM(cd9660node *,const char *);
+
+/* Parent and child link function */
+int cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+
+int cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+
+
+/*
+ * Relocation directory function. I'm not quite sure what
+ * sort of parameters are needed, but personally I don't think
+ * any parameters are needed except for the memory address where
+ * the information needs to be put in
+ */
+int cd9660node_rrip_re(void *, fsnode *);
+
+/*
+ * Don't know if this function is needed because it apparently is an
+ * optional feature that does not really need to be implemented but I
+ * thought I should add it anyway.
+ */
+int cd9660_susp_ce (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_susp_pd (struct ISO_SUSP_ATTRIBUTES *, int);
+int cd9660_susp_sp (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_susp_st (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+
+struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ER(cd9660node *, u_char, const char *,
+    const char *, const char *);
+struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES*,
+    cd9660node *);
+
+
+/* Helper functions */
+
+/* Common SUSP/RRIP functions */
+int cd9660_susp_initialize(iso9660_disk *, cd9660node *, cd9660node *,
+    cd9660node *);
+int cd9660_susp_initialize_node(iso9660_disk *, cd9660node *);
+struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_create_node(int, int, const char *,
+    int);
+struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_add_entry(cd9660node *,
+    struct ISO_SUSP_ATTRIBUTES *, struct ISO_SUSP_ATTRIBUTES *, int);
+
+/* RRIP specific functions */
+int cd9660_rrip_initialize_node(iso9660_disk *, cd9660node *, cd9660node *,
+    cd9660node *);
+void cd9660_createSL(cd9660node *);
+
+/* Functions that probably can be removed */
+/* int cd9660node_initialize_node(int, char *); */
+
+
+#endif
diff --git a/usr.sbin/makefs/chfs.c b/usr.sbin/makefs/chfs.c
new file mode 100644 (file)
index 0000000..de14246
--- /dev/null
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 2012 Department of Software Engineering,
+ *                   University of Szeged, Hungary
+ * Copyright (c) 2012 Tamas Toth <ttoth@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "chfs_makefs.h"
+
+#include "chfs/chfs_mkfs.h"
+
+static void chfs_validate(const char *, fsnode *, fsinfo_t *);
+static int chfs_create_image(const char *, fsinfo_t *);
+static int chfs_populate_dir(const char *, fsnode *, fsnode *, fsinfo_t *);
+
+
+void
+chfs_prep_opts(fsinfo_t *fsopts)
+{
+       chfs_opt_t *chfs_opts = ecalloc(1, sizeof(*chfs_opts));
+
+       const option_t chfs_options[] = {
+               { 'p', "pagesize", &chfs_opts->pagesize, OPT_INT32,
+                 1, INT_MAX, "page size" },
+               { 'e', "eraseblock", &chfs_opts->eraseblock, OPT_INT32,
+                 1, INT_MAX, "eraseblock size" },
+               { 'm', "mediatype", &chfs_opts->mediatype, OPT_INT32,
+                 0, 1, "type of the media, 0 (nor) or 1 (nand)" },
+               { .name = NULL }
+       };
+
+       chfs_opts->pagesize = -1;
+       chfs_opts->eraseblock = -1;
+       chfs_opts->mediatype = -1;
+
+       fsopts->size = 0;
+       fsopts->fs_specific = chfs_opts;
+       fsopts->fs_options = copy_opts(chfs_options);
+}
+
+void
+chfs_cleanup_opts(fsinfo_t *fsopts)
+{
+       free(fsopts->fs_specific);
+       free(fsopts->fs_options);
+}
+
+int
+chfs_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+
+       assert(option != NULL);
+       assert(fsopts != NULL);
+
+       return set_option(fsopts->fs_options, option, NULL, 0) != -1;
+}
+
+void
+chfs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+       struct timeval  start;
+
+       assert(image != NULL);
+       assert(dir != NULL);
+       assert(root != NULL);
+       assert(fsopts != NULL); 
+
+       TIMER_START(start);
+       chfs_validate(dir, root, fsopts);
+       TIMER_RESULTS(start, "chfs_validate");
+
+       printf("Creating `%s'\n", image);
+       TIMER_START(start);
+       if (chfs_create_image(image, fsopts) == -1) {
+               errx(EXIT_FAILURE, "Image file `%s' not created", image);
+       }
+       TIMER_RESULTS(start, "chfs_create_image");
+       
+       fsopts->curinode = CHFS_ROOTINO;
+       root->inode->ino = CHFS_ROOTINO;
+
+       printf("Populating `%s'\n", image);
+       TIMER_START(start);
+       write_eb_header(fsopts);
+       if (!chfs_populate_dir(dir, root, root, fsopts)) {
+               errx(EXIT_FAILURE, "Image file `%s' not populated", image);
+       }
+       TIMER_RESULTS(start, "chfs_populate_dir");
+
+       padblock(fsopts);
+
+       if (close(fsopts->fd) == -1) {
+               err(EXIT_FAILURE, "Closing `%s'", image);
+       }
+       fsopts->fd = -1;
+
+       printf("Image `%s' complete\n", image);
+}
+
+static void
+chfs_validate(const char* dir, fsnode *root, fsinfo_t *fsopts)
+{
+       chfs_opt_t *chfs_opts;
+       assert(dir != NULL);
+       assert(root != NULL);
+       assert(fsopts != NULL);
+
+       chfs_opts = fsopts->fs_specific;
+
+       if (chfs_opts->pagesize == -1) {
+               chfs_opts->pagesize = DEFAULT_PAGESIZE;
+       }
+       if (chfs_opts->eraseblock == -1) {
+               chfs_opts->eraseblock = DEFAULT_ERASEBLOCK;
+       }
+       if (chfs_opts->mediatype == -1) {
+               chfs_opts->mediatype = DEFAULT_MEDIATYPE;
+       }
+}
+
+static int
+chfs_create_image(const char *image, fsinfo_t *fsopts)
+{
+       assert(image != NULL);
+       assert(fsopts != NULL);
+       
+       if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) {
+               warn("Can't open `%s' for writing", image);
+               return -1;
+       }
+
+       return fsopts->fd;
+}
+
+static int
+chfs_populate_dir(const char *dir, fsnode *root, fsnode *parent,
+    fsinfo_t *fsopts)
+{
+       fsnode *cur;
+       char path[MAXPATHLEN + 1];
+
+       assert(dir != NULL);
+       assert(root != NULL);
+       assert(fsopts != NULL); 
+       
+       for (cur = root->next; cur != NULL; cur = cur->next) {
+               if ((cur->inode->flags & FI_ALLOCATED) == 0) {
+                       cur->inode->flags |= FI_ALLOCATED;
+                       if (cur != root) {
+                               fsopts->curinode++;
+                               cur->inode->ino = fsopts->curinode;
+                               cur->parent = parent;
+                       }
+               }
+
+               if (cur->inode->flags & FI_WRITTEN) {
+                       continue;       // hard link
+               }
+               cur->inode->flags |= FI_WRITTEN;
+
+               write_vnode(fsopts, cur);
+               write_dirent(fsopts, cur);
+               if (!S_ISDIR(cur->type & S_IFMT)) {
+                       write_file(fsopts, cur, dir);
+               }
+       }
+       
+       for (cur = root; cur != NULL; cur = cur->next) {
+               if (cur->child == NULL) {
+                       continue;
+               }
+               if ((size_t)snprintf(path, sizeof(path), "%s/%s", dir,
+                   cur->name) >= sizeof(path)) {
+                       errx(EXIT_FAILURE, "Pathname too long");
+               }
+               if (!chfs_populate_dir(path, cur->child, cur, fsopts)) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
diff --git a/usr.sbin/makefs/chfs/Makefile.inc b/usr.sbin/makefs/chfs/Makefile.inc
new file mode 100644 (file)
index 0000000..254c4a3
--- /dev/null
@@ -0,0 +1,11 @@
+CHFS=  ${NETBSDSRCDIR}/sys/ufs/chfs
+
+.PATH: ${.CURDIR}/chfs ${NETBSDSRCDIR}/sys/ufs/chfs
+
+CPPFLAGS+= -I${CHFS}
+
+SRCS+= chfs_mkfs.c
+.if !defined(HOSTPROG)
+LDADD+= -lz
+DPADD+= ${LIBZ}
+.endif
diff --git a/usr.sbin/makefs/chfs/chfs_mkfs.c b/usr.sbin/makefs/chfs/chfs_mkfs.c
new file mode 100644 (file)
index 0000000..4c96f66
--- /dev/null
@@ -0,0 +1,307 @@
+/*-
+ * Copyright (c) 2012 Department of Software Engineering,
+ *                   University of Szeged, Hungary
+ * Copyright (c) 2012 Tamas Toth <ttoth@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include <util.h>
+
+#if defined(__minix)
+#include <unistd.h>
+#endif
+
+#include "makefs.h"
+#include "chfs_makefs.h"
+
+#include "media.h"
+#include "ebh.h"
+
+#include "chfs/chfs_mkfs.h"
+
+static uint32_t img_ofs = 0;
+static uint64_t version = 0;
+static uint64_t max_serial = 0;
+static int lebnumber = 0;
+
+static const unsigned char ffbuf[16] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static void
+buf_write(fsinfo_t *fsopts, const void *buf, size_t len)
+{
+       ssize_t retval;
+       const char *charbuf = buf;
+
+       while (len > 0) {
+               retval = write(fsopts->fd, charbuf, len);
+
+               if (retval == -1) {
+                       err(EXIT_FAILURE, "ERROR while writing");
+               }
+
+               len -= retval;
+               charbuf += retval;
+               img_ofs += retval;
+       }
+}
+
+void
+padblock(fsinfo_t *fsopts)
+{
+       chfs_opt_t *chfs_opts = fsopts->fs_specific;
+       while (img_ofs % chfs_opts->eraseblock) {
+               buf_write(fsopts, ffbuf, MIN(sizeof(ffbuf),
+                   chfs_opts->eraseblock - (img_ofs % chfs_opts->eraseblock)));
+       }
+}
+
+static void
+padword(fsinfo_t *fsopts)
+{
+       if (img_ofs % 4) {
+               buf_write(fsopts, ffbuf, 4 - img_ofs % 4);
+       }
+}
+
+static void
+pad_block_if_less_than(fsinfo_t *fsopts, int req)
+{
+       chfs_opt_t *chfs_opts = fsopts->fs_specific;
+       if ((img_ofs % chfs_opts->eraseblock) + req >
+           (uint32_t)chfs_opts->eraseblock) {
+               padblock(fsopts);
+               write_eb_header(fsopts);
+       }
+}
+
+void
+write_eb_header(fsinfo_t *fsopts)
+{
+       chfs_opt_t *opts;
+       struct chfs_eb_hdr ebhdr;
+       char *buf;
+
+       opts = fsopts->fs_specific;
+
+#define MINSIZE MAX(MAX(CHFS_EB_EC_HDR_SIZE, CHFS_EB_HDR_NOR_SIZE), \
+    CHFS_EB_HDR_NAND_SIZE)
+       if ((uint32_t)opts->pagesize < MINSIZE)
+               errx(EXIT_FAILURE, "pagesize cannot be less than %zu", MINSIZE);
+       buf = emalloc(opts->pagesize);
+       memset(buf, 0xFF, opts->pagesize);
+
+       ebhdr.ec_hdr.magic = htole32(CHFS_MAGIC_BITMASK);
+       ebhdr.ec_hdr.erase_cnt = htole32(1);
+       ebhdr.ec_hdr.crc_ec = htole32(crc32(0,
+           (uint8_t *)&ebhdr.ec_hdr + 8, 4));
+
+       memcpy(buf, &ebhdr.ec_hdr, CHFS_EB_EC_HDR_SIZE);
+
+       buf_write(fsopts, buf, opts->pagesize);
+
+       memset(buf, 0xFF, opts->pagesize);
+
+       if (opts->mediatype == TYPE_NAND) {
+               ebhdr.u.nand_hdr.lid = htole32(lebnumber++);
+               ebhdr.u.nand_hdr.serial = htole64(++(max_serial));
+               ebhdr.u.nand_hdr.crc = htole32(crc32(0,
+                   (uint8_t *)&ebhdr.u.nand_hdr + 4,
+                   CHFS_EB_HDR_NAND_SIZE - 4));
+               memcpy(buf, &ebhdr.u.nand_hdr, CHFS_EB_HDR_NAND_SIZE);
+       } else {
+               ebhdr.u.nor_hdr.lid = htole32(lebnumber++);
+               ebhdr.u.nor_hdr.crc = htole32(crc32(0,
+                   (uint8_t *)&ebhdr.u.nor_hdr + 4,
+                   CHFS_EB_HDR_NOR_SIZE - 4));
+               memcpy(buf, &ebhdr.u.nor_hdr, CHFS_EB_HDR_NOR_SIZE);
+       }
+       
+       buf_write(fsopts, buf, opts->pagesize);
+       free(buf);
+}
+
+void
+write_vnode(fsinfo_t *fsopts, fsnode *node)
+{
+       struct chfs_flash_vnode fvnode;
+       memset(&fvnode, 0, sizeof(fvnode));
+       
+       fvnode.magic = htole16(CHFS_FS_MAGIC_BITMASK);
+       fvnode.type = htole16(CHFS_NODETYPE_VNODE);
+       fvnode.length = htole32(CHFS_PAD(sizeof(fvnode)));
+       fvnode.hdr_crc = htole32(crc32(0, (uint8_t *)&fvnode,
+           CHFS_NODE_HDR_SIZE - 4));
+       fvnode.vno = htole64(node->inode->ino);
+       fvnode.version = htole64(version++);
+       fvnode.mode = htole32(node->inode->st.st_mode);
+       fvnode.dn_size = htole32(node->inode->st.st_size);
+       fvnode.atime = htole32(node->inode->st.st_atime);
+       fvnode.ctime = htole32(node->inode->st.st_ctime);
+       fvnode.mtime = htole32(node->inode->st.st_mtime);
+       fvnode.gid = htole32(node->inode->st.st_uid);
+       fvnode.uid = htole32(node->inode->st.st_gid);
+       fvnode.node_crc = htole32(crc32(0, (uint8_t *)&fvnode,
+           sizeof(fvnode) - 4));
+
+       pad_block_if_less_than(fsopts, sizeof(fvnode));
+       buf_write(fsopts, &fvnode, sizeof(fvnode));
+       padword(fsopts);
+}
+
+void
+write_dirent(fsinfo_t *fsopts, fsnode *node)
+{
+       struct chfs_flash_dirent_node fdirent;
+       char *name;
+
+       name = emalloc(strlen(node->name));
+       memcpy(name, node->name, strlen(node->name));
+
+       memset(&fdirent, 0, sizeof(fdirent));
+       fdirent.magic = htole16(CHFS_FS_MAGIC_BITMASK);
+       fdirent.type = htole16(CHFS_NODETYPE_DIRENT);
+       fdirent.length = htole32(CHFS_PAD(sizeof(fdirent) + strlen(name)));
+       fdirent.hdr_crc = htole32(crc32(0, (uint8_t *)&fdirent,
+           CHFS_NODE_HDR_SIZE - 4));
+       fdirent.vno = htole64(node->inode->ino);
+       if (node->parent != NULL) {
+               fdirent.pvno = htole64(node->parent->inode->ino);
+       } else {
+               fdirent.pvno = htole64(node->inode->ino);
+       }
+
+       fdirent.version = htole64(version++);
+       fdirent.mctime = 0;
+       fdirent.nsize = htole32(strlen(name));
+       fdirent.dtype = htole32(IFTOCHT(node->type & S_IFMT));
+       fdirent.name_crc = htole32(crc32(0, (uint8_t *)name, fdirent.nsize));
+       fdirent.node_crc = htole32(crc32(0, (uint8_t *)&fdirent,
+           sizeof(fdirent) - 4));
+       
+       pad_block_if_less_than(fsopts, sizeof(fdirent) + fdirent.nsize);
+       buf_write(fsopts, &fdirent, sizeof(fdirent));
+       buf_write(fsopts, name, fdirent.nsize);
+       padword(fsopts);
+}
+
+void
+write_file(fsinfo_t *fsopts, fsnode *node, const char *dir)
+{
+       int fd;
+       ssize_t len;
+       char *name = node->name;
+       chfs_opt_t *opts;
+       unsigned char *buf;
+       uint32_t fileofs = 0;
+
+       opts = fsopts->fs_specific;
+       buf = emalloc(opts->pagesize);
+       if (node->type == S_IFREG || node->type == S_IFSOCK) {
+               char *longname;
+               if (asprintf(&longname, "%s/%s", dir, name) == 1)
+                       goto out;
+
+               fd = open(longname, O_RDONLY, 0444);
+               if (fd == -1)
+                       err(EXIT_FAILURE, "Cannot open `%s'", longname);
+
+               while ((len = read(fd, buf, opts->pagesize))) {
+                       if (len < 0) {
+                               warn("ERROR while reading %s", longname);
+                               free(longname);
+                               free(buf);
+                               close(fd);
+                               return;
+                       }
+
+                       write_data(fsopts, node, buf, len, fileofs);
+                       fileofs += len;
+               }
+               free(longname);
+               close(fd);      
+       } else if (node->type == S_IFLNK) {
+               len = strlen(node->symlink);
+               memcpy(buf, node->symlink, len);
+               write_data(fsopts, node, buf, len, 0);
+       } else if (node->type == S_IFCHR || node->type == S_IFBLK ||
+               node->type == S_IFIFO) {
+               len = sizeof(dev_t);
+               memcpy(buf, &(node->inode->st.st_rdev), len);
+               write_data(fsopts, node, buf, len, 0);
+       }
+
+       free(buf);
+       return;
+out:
+       err(EXIT_FAILURE, "Memory allocation failed");
+}
+
+void
+write_data(fsinfo_t *fsopts, fsnode *node, unsigned char *buf, size_t len,
+    uint32_t ofs)
+{
+       struct chfs_flash_data_node fdata;
+
+       memset(&fdata, 0, sizeof(fdata));
+       if (len == 0) {
+               return;
+       }
+       
+       pad_block_if_less_than(fsopts, sizeof(fdata) + len);
+
+       fdata.magic = htole16(CHFS_FS_MAGIC_BITMASK);
+       fdata.type = htole16(CHFS_NODETYPE_DATA);
+       fdata.length = htole32(CHFS_PAD(sizeof(fdata) + len));
+       fdata.hdr_crc = htole32(crc32(0, (uint8_t *)&fdata,
+           CHFS_NODE_HDR_SIZE - 4));
+       fdata.vno = htole64(node->inode->ino);
+       fdata.data_length = htole32(len);
+       fdata.offset = htole32(ofs);
+       fdata.data_crc = htole32(crc32(0, (uint8_t *)buf, len));
+       fdata.node_crc = htole32(crc32(0,
+           (uint8_t *)&fdata, sizeof(fdata) - 4));
+
+       buf_write(fsopts, &fdata, sizeof(fdata));
+       buf_write(fsopts, buf, len);
+       padword(fsopts);
+}
diff --git a/usr.sbin/makefs/chfs/chfs_mkfs.h b/usr.sbin/makefs/chfs/chfs_mkfs.h
new file mode 100644 (file)
index 0000000..8322992
--- /dev/null
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2012 Department of Software Engineering,
+ *                   University of Szeged, Hungary
+ * Copyright (c) 2012 Tamas Toth <ttoth@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef _CHFS_MKFS_H
+#define _CHFS_MKFS_H
+
+#include "chfs.h"
+
+void padblock(fsinfo_t *);
+void write_eb_header(fsinfo_t *);
+void write_vnode(fsinfo_t *, fsnode *);
+void write_dirent(fsinfo_t *, fsnode *);
+void write_file(fsinfo_t *, fsnode *, const char *);
+void write_data(fsinfo_t *, fsnode *, unsigned char *, size_t, uint32_t);
+#endif
+
diff --git a/usr.sbin/makefs/chfs_makefs.h b/usr.sbin/makefs/chfs_makefs.h
new file mode 100644 (file)
index 0000000..e722a9f
--- /dev/null
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2012 Department of Software Engineering,
+ *                   University of Szeged, Hungary
+ * Copyright (c) 2012 Tamas Toth <ttoth@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef _CHFS_MAKEFS_H
+#define _CHFS_MAKEFS_H
+
+#define TYPE_NOR 0
+#define TYPE_NAND 1
+
+#define DEFAULT_PAGESIZE 2048
+#define DEFAULT_ERASEBLOCK 131072
+#define DEFAULT_MEDIATYPE TYPE_NAND
+
+typedef struct {
+       int pagesize;           /* page size */
+       int eraseblock;         /* eraseblock size */
+       int mediatype;          /* type of the media, 0 (nor) or 1 (nand) */
+} chfs_opt_t;
+
+#endif
diff --git a/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c
new file mode 100644 (file)
index 0000000..723c213
--- /dev/null
@@ -0,0 +1,1155 @@
+/*     $NetBSD: ffs.c,v 1.63 2013/06/23 02:06:06 dholland Exp $        */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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) 1982, 1986, 1989, 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.
+ *
+ *     @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: ffs.c,v 1.63 2013/06/23 02:06:06 dholland Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/mount.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "ffs.h"
+
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+#include <sys/statvfs.h>
+#endif
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/ufs_bswap.h>
+
+#include "ffs/ufs_inode.h"
+#include "ffs/newfs_extern.h"
+#include "ffs/ffs_extern.h"
+
+#undef DIP
+#define DIP(dp, field) \
+       ((ffs_opts->version == 1) ? \
+       (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field)
+
+/*
+ * Various file system defaults (cribbed from newfs(8)).
+ */
+#define        DFL_FRAGSIZE            1024            /* fragment size */
+#define        DFL_BLKSIZE             8192            /* block size */
+#define        DFL_SECSIZE             512             /* sector size */
+#define        DFL_CYLSPERGROUP        65536           /* cylinders per group */
+#define        DFL_FRAGSPERINODE       4               /* fragments per inode */
+#define        DFL_ROTDELAY            0               /* rotational delay */
+#define        DFL_NRPOS               1               /* rotational positions */
+#define        DFL_RPM                 3600            /* rpm of disk */
+#define        DFL_NSECTORS            64              /* # of sectors */
+#define        DFL_NTRACKS             16              /* # of tracks */
+
+
+typedef struct {
+       u_char          *buf;           /* buf for directory */
+       doff_t          size;           /* full size of buf */
+       doff_t          cur;            /* offset of current entry */
+} dirbuf_t;
+
+
+static int     ffs_create_image(const char *, fsinfo_t *);
+static void    ffs_dump_fsinfo(fsinfo_t *);
+static void    ffs_dump_dirbuf(dirbuf_t *, const char *, int);
+static void    ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int);
+static int     ffs_populate_dir(const char *, fsnode *, fsinfo_t *);
+static void    ffs_size_dir(fsnode *, fsinfo_t *);
+static void    ffs_validate(const char *, fsnode *, fsinfo_t *);
+static void    ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *);
+static void    ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *);
+static  void   *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *,
+                                fsnode *, fsinfo_t *);
+static  void   *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *,
+                                fsnode *, fsinfo_t *);
+
+
+
+       /* publically visible functions */
+void
+ffs_prep_opts(fsinfo_t *fsopts)
+{
+       ffs_opt_t *ffs_opts = ecalloc(1, sizeof(*ffs_opts));
+
+       const option_t ffs_options[] = {
+           { 'b', "bsize", &ffs_opts->bsize, OPT_INT32,
+             1, INT_MAX, "block size" },
+           { 'f', "fsize", &ffs_opts->fsize, OPT_INT32,
+             1, INT_MAX, "fragment size" },
+           { 'd', "density", &ffs_opts->density, OPT_INT32,
+             1, INT_MAX, "bytes per inode" },
+           { 'm', "minfree", &ffs_opts->minfree, OPT_INT32,
+             0, 99, "minfree" },
+           { 'M', "maxbpf", &ffs_opts->maxbpg, OPT_INT32,
+             1, INT_MAX, "max blocks per file in a cg" },
+           { 'a', "avgfilesize", &ffs_opts->avgfilesize, OPT_INT32,
+             1, INT_MAX, "expected average file size" },
+           { 'n', "avgfpdir", &ffs_opts->avgfpdir, OPT_INT32,
+             1, INT_MAX, "expected # of files per directory" },
+           { 'x', "extent", &ffs_opts->maxbsize, OPT_INT32,
+             1, INT_MAX, "maximum # extent size" },
+           { 'g', "maxbpcg", &ffs_opts->maxblkspercg, OPT_INT32,
+             1, INT_MAX, "max # of blocks per group" },
+           { 'v', "version", &ffs_opts->version, OPT_INT32,
+             1, 2, "UFS version" },
+           { 'o', "optimization", NULL, OPT_STRBUF,
+             0, 0, "Optimization (time|space)" },
+           { 'l', "label", ffs_opts->label, OPT_STRARRAY,
+             1, sizeof(ffs_opts->label), "UFS label" },
+           { .name = NULL }
+       };
+
+       ffs_opts->bsize= -1;
+       ffs_opts->fsize= -1;
+       ffs_opts->cpg= -1;
+       ffs_opts->density= -1;
+       ffs_opts->minfree= -1;
+       ffs_opts->optimization= -1;
+       ffs_opts->maxcontig= -1;
+       ffs_opts->maxbpg= -1;
+       ffs_opts->avgfilesize= -1;
+       ffs_opts->avgfpdir= -1;
+       ffs_opts->version = 1;
+
+       fsopts->fs_specific = ffs_opts;
+       fsopts->fs_options = copy_opts(ffs_options);
+}
+
+void
+ffs_cleanup_opts(fsinfo_t *fsopts)
+{
+       free(fsopts->fs_specific);
+       free(fsopts->fs_options);
+}
+
+int
+ffs_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+       ffs_opt_t       *ffs_opts = fsopts->fs_specific;
+       option_t *ffs_options = fsopts->fs_options;
+       char buf[1024];
+
+       int     rv;
+
+       assert(option != NULL);
+       assert(fsopts != NULL);
+       assert(ffs_opts != NULL);
+
+       if (debug & DEBUG_FS_PARSE_OPTS)
+               printf("ffs_parse_opts: got `%s'\n", option);
+
+       rv = set_option(ffs_options, option, buf, sizeof(buf));
+       if (rv == -1)
+               return 0;
+
+       if (ffs_options[rv].name == NULL)
+               abort();
+
+       switch (ffs_options[rv].letter) {
+       case 'o':
+               if (strcmp(buf, "time") == 0) {
+                       ffs_opts->optimization = FS_OPTTIME;
+               } else if (strcmp(buf, "space") == 0) {
+                       ffs_opts->optimization = FS_OPTSPACE;
+               } else {
+                       warnx("Invalid optimization `%s'", buf);
+                       return 0;
+               }
+               break;
+       default:
+               break;
+       }
+       return 1;
+}
+
+
+void
+ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+       struct fs       *superblock;
+       struct timeval  start;
+
+       assert(image != NULL);
+       assert(dir != NULL);
+       assert(root != NULL);
+       assert(fsopts != NULL);
+
+       if (debug & DEBUG_FS_MAKEFS)
+               printf("ffs_makefs: image %s directory %s root %p\n",
+                   image, dir, root);
+
+               /* validate tree and options */
+       TIMER_START(start);
+       ffs_validate(dir, root, fsopts);
+       TIMER_RESULTS(start, "ffs_validate");
+
+       printf("Calculated size of `%s': %lld bytes, %lld inodes\n",
+           image, (long long)fsopts->size, (long long)fsopts->inodes);
+
+               /* create image */
+       TIMER_START(start);
+       if (ffs_create_image(image, fsopts) == -1)
+               errx(1, "Image file `%s' not created.", image);
+       TIMER_RESULTS(start, "ffs_create_image");
+
+       fsopts->curinode = UFS_ROOTINO;
+
+       if (debug & DEBUG_FS_MAKEFS)
+               putchar('\n');
+
+               /* populate image */
+       printf("Populating `%s'\n", image);
+       TIMER_START(start);
+       if (! ffs_populate_dir(dir, root, fsopts))
+               errx(1, "Image file `%s' not populated.", image);
+       TIMER_RESULTS(start, "ffs_populate_dir");
+
+               /* ensure no outstanding buffers remain */
+       if (debug & DEBUG_FS_MAKEFS)
+               bcleanup();
+
+               /* update various superblock parameters */
+       superblock = fsopts->superblock;
+       superblock->fs_fmod = 0;
+       superblock->fs_old_cstotal.cs_ndir   = superblock->fs_cstotal.cs_ndir;
+       superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree;
+       superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree;
+       superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree;
+
+               /* write out superblock; image is now complete */
+       ffs_write_superblock(fsopts->superblock, fsopts);
+       if (close(fsopts->fd) == -1)
+               err(1, "Closing `%s'", image);
+       fsopts->fd = -1;
+       printf("Image `%s' complete\n", image);
+}
+
+       /* end of public functions */
+
+
+static void
+ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+       int32_t ncg = 1;
+#if notyet
+       int32_t spc, nspf, ncyl, fssize;
+#endif
+       ffs_opt_t       *ffs_opts = fsopts->fs_specific;
+
+       assert(dir != NULL);
+       assert(root != NULL);
+       assert(fsopts != NULL);
+       assert(ffs_opts != NULL);
+
+       if (debug & DEBUG_FS_VALIDATE) {
+               printf("ffs_validate: before defaults set:\n");
+               ffs_dump_fsinfo(fsopts);
+       }
+
+               /* set FFS defaults */
+       if (fsopts->sectorsize == -1)
+               fsopts->sectorsize = DFL_SECSIZE;
+       if (ffs_opts->fsize == -1)
+               ffs_opts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize);
+       if (ffs_opts->bsize == -1)
+               ffs_opts->bsize = MIN(DFL_BLKSIZE, 8 * ffs_opts->fsize);
+       if (ffs_opts->cpg == -1)
+               ffs_opts->cpg = DFL_CYLSPERGROUP;
+       else
+               ffs_opts->cpgflg = 1;
+                               /* fsopts->density is set below */
+       if (ffs_opts->nsectors == -1)
+               ffs_opts->nsectors = DFL_NSECTORS;
+       if (ffs_opts->minfree == -1)
+               ffs_opts->minfree = MINFREE;
+       if (ffs_opts->optimization == -1)
+               ffs_opts->optimization = DEFAULTOPT;
+       if (ffs_opts->maxcontig == -1)
+               ffs_opts->maxcontig =
+                   MAX(1, MIN(MAXPHYS, FFS_MAXBSIZE) / ffs_opts->bsize);
+       /* XXX ondisk32 */
+       if (ffs_opts->maxbpg == -1)
+               ffs_opts->maxbpg = ffs_opts->bsize / sizeof(int32_t);
+       if (ffs_opts->avgfilesize == -1)
+               ffs_opts->avgfilesize = AVFILESIZ;
+       if (ffs_opts->avgfpdir == -1)
+               ffs_opts->avgfpdir = AFPDIR;
+
+               /* calculate size of tree */
+       ffs_size_dir(root, fsopts);
+       fsopts->inodes += UFS_ROOTINO;          /* include first two inodes */
+
+       if (debug & DEBUG_FS_VALIDATE)
+               printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n",
+                   (long long)fsopts->size, (long long)fsopts->inodes);
+
+               /* add requested slop */
+       fsopts->size += fsopts->freeblocks;
+       fsopts->inodes += fsopts->freefiles;
+       if (fsopts->freefilepc > 0)
+               fsopts->inodes =
+                   fsopts->inodes * (100 + fsopts->freefilepc) / 100;
+       if (fsopts->freeblockpc > 0)
+               fsopts->size =
+                   fsopts->size * (100 + fsopts->freeblockpc) / 100;
+
+               /* add space needed for superblocks */
+       /*
+        * The old SBOFF (SBLOCK_UFS1) is used here because makefs is
+        * typically used for small filesystems where space matters.
+        * XXX make this an option.
+        */
+       fsopts->size += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg;
+               /* add space needed to store inodes, x3 for blockmaps, etc */
+       if (ffs_opts->version == 1)
+               fsopts->size += ncg * DINODE1_SIZE *
+                   roundup(fsopts->inodes / ncg, 
+                       ffs_opts->bsize / DINODE1_SIZE);
+       else
+               fsopts->size += ncg * DINODE2_SIZE *
+                   roundup(fsopts->inodes / ncg, 
+                       ffs_opts->bsize / DINODE2_SIZE);
+
+               /* add minfree */
+       if (ffs_opts->minfree > 0)
+               fsopts->size =
+                   fsopts->size * (100 + ffs_opts->minfree) / 100;
+       /*
+        * XXX  any other fs slop to add, such as csum's, bitmaps, etc ??
+        */
+
+       if (fsopts->size < fsopts->minsize)     /* ensure meets minimum size */
+               fsopts->size = fsopts->minsize;
+
+               /* round up to the next block */
+       fsopts->size = roundup(fsopts->size, ffs_opts->bsize);
+
+               /* calculate density if necessary */
+       if (ffs_opts->density == -1)
+               ffs_opts->density = fsopts->size / fsopts->inodes + 1;
+
+       if (debug & DEBUG_FS_VALIDATE) {
+               printf("ffs_validate: after defaults set:\n");
+               ffs_dump_fsinfo(fsopts);
+               printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n",
+                   dir, (long long)fsopts->size, (long long)fsopts->inodes);
+       }
+               /* now check calculated sizes vs requested sizes */
+       if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) {
+               errx(1, "`%s' size of %lld is larger than the maxsize of %lld.",
+                   dir, (long long)fsopts->size, (long long)fsopts->maxsize);
+       }
+}
+
+
+static void
+ffs_dump_fsinfo(fsinfo_t *f)
+{
+
+       ffs_opt_t       *fs = f->fs_specific;
+
+       printf("fsopts at %p\n", f);
+
+       printf("\tsize %lld, inodes %lld, curinode %u\n",
+           (long long)f->size, (long long)f->inodes, f->curinode);
+
+       printf("\tminsize %lld, maxsize %lld\n",
+           (long long)f->minsize, (long long)f->maxsize);
+       printf("\tfree files %lld, freefile %% %d\n",
+           (long long)f->freefiles, f->freefilepc);
+       printf("\tfree blocks %lld, freeblock %% %d\n",
+           (long long)f->freeblocks, f->freeblockpc);
+       printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize);
+
+       printf("\tbsize %d, fsize %d, cpg %d, density %d\n",
+           fs->bsize, fs->fsize, fs->cpg, fs->density);
+       printf("\tnsectors %d, rpm %d, minfree %d\n",
+           fs->nsectors, fs->rpm, fs->minfree);
+       printf("\tmaxcontig %d, maxbpg %d\n",
+           fs->maxcontig, fs->maxbpg);
+       printf("\toptimization %s\n",
+           fs->optimization == FS_OPTSPACE ? "space" : "time");
+}
+
+
+static int
+ffs_create_image(const char *image, fsinfo_t *fsopts)
+{
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+       struct statvfs  sfs;
+#endif
+       struct fs       *fs;
+       char    *buf;
+       int     i, bufsize;
+       off_t   bufrem;
+       int     oflags = O_RDWR | O_CREAT;
+
+       assert (image != NULL);
+       assert (fsopts != NULL);
+
+               /* create image */
+       if (fsopts->offset == 0)
+               oflags |= O_TRUNC;
+       if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
+               warn("Can't open `%s' for writing", image);
+               return (-1);
+       }
+
+               /* zero image */
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+       if (fstatvfs(fsopts->fd, &sfs) == -1) {
+#endif
+               bufsize = 8192;
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+               warn("can't fstatvfs `%s', using default %d byte chunk",
+                   image, bufsize);
+       } else
+               bufsize = sfs.f_iosize;
+#endif
+       bufrem = fsopts->size;
+
+       if (fsopts->sparse) {
+               if (ftruncate(fsopts->fd, bufrem) == -1) {
+                       printf ("ERROR in truncate. Sparse option disabled\n");
+                       fsopts->sparse = 0;
+               } else {
+                       bufrem = 0; /* File truncated at bufrem. Remaining is 0 */
+                       buf = NULL;
+               }
+       }
+
+       if (fsopts->offset != 0)
+               if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
+                       warn("can't seek");
+                       return -1;
+               }
+
+       if ((debug & DEBUG_FS_CREATE_IMAGE) && fsopts->sparse == 0)
+               printf(
+                   "zero-ing image `%s', %lld sectors, using %d byte chunks\n",
+                   image, (long long)bufrem, bufsize);
+       if (bufrem > 0)
+               buf = ecalloc(1, bufsize);
+       while (bufrem > 0) {
+               i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
+               if (i == -1) {
+                       warn("zeroing image, %lld bytes to go",
+                           (long long)bufrem);
+                       free(buf);
+                       return (-1);
+               }
+               bufrem -= i;
+       }
+       if (buf)
+               free(buf);
+
+               /* make the file system */
+       if (debug & DEBUG_FS_CREATE_IMAGE)
+               printf("calling mkfs(\"%s\", ...)\n", image);
+       fs = ffs_mkfs(image, fsopts);
+       fsopts->superblock = (void *)fs;
+       if (debug & DEBUG_FS_CREATE_IMAGE) {
+               time_t t;
+
+               t = (time_t)((struct fs *)fsopts->superblock)->fs_time;
+               printf("mkfs returned %p; fs_time %s",
+                   fsopts->superblock, ctime(&t));
+               printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n",
+                   (long long)fs->fs_cstotal.cs_nbfree,
+                   (long long)fs->fs_cstotal.cs_nffree,
+                   (long long)fs->fs_cstotal.cs_nifree,
+                   (long long)fs->fs_cstotal.cs_ndir);
+       }
+
+       if ((off_t)(fs->fs_cstotal.cs_nifree + UFS_ROOTINO) < fsopts->inodes) {
+               warnx(
+               "Image file `%s' has %lld free inodes; %lld are required.",
+                   image,
+                   (long long)(fs->fs_cstotal.cs_nifree + UFS_ROOTINO),
+                   (long long)fsopts->inodes);
+               return (-1);
+       }
+       return (fsopts->fd);
+}
+
+
+static void
+ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
+{
+       struct direct   tmpdir;
+       fsnode *        node;
+       int             curdirsize, this;
+       ffs_opt_t       *ffs_opts = fsopts->fs_specific;
+
+       /* node may be NULL (empty directory) */
+       assert(fsopts != NULL);
+       assert(ffs_opts != NULL);
+
+       if (debug & DEBUG_FS_SIZE_DIR)
+               printf("ffs_size_dir: entry: bytes %lld inodes %lld\n",
+                   (long long)fsopts->size, (long long)fsopts->inodes);
+
+#define        ADDDIRENT(e) do {                                               \
+       tmpdir.d_namlen = strlen((e));                                  \
+       this = UFS_DIRSIZ(0, &tmpdir, 0);                               \
+       if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT)                       \
+               printf("ADDDIRENT: was: %s (%d) this %d cur %d\n",      \
+                   e, tmpdir.d_namlen, this, curdirsize);              \
+       if (this + curdirsize > roundup(curdirsize, UFS_DIRBLKSIZ))     \
+               curdirsize = roundup(curdirsize, UFS_DIRBLKSIZ);        \
+       curdirsize += this;                                             \
+       if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT)                       \
+               printf("ADDDIRENT: now: %s (%d) this %d cur %d\n",      \
+                   e, tmpdir.d_namlen, this, curdirsize);              \
+} while (0);
+
+       /*
+        * XXX  this needs to take into account extra space consumed
+        *      by indirect blocks, etc.
+        */
+#define        ADDSIZE(x) do {                                                 \
+       fsopts->size += roundup((x), ffs_opts->fsize);                  \
+} while (0);
+
+       curdirsize = 0;
+       for (node = root; node != NULL; node = node->next) {
+               ADDDIRENT(node->name);
+               if (node == root) {                     /* we're at "." */
+                       assert(strcmp(node->name, ".") == 0);
+                       ADDDIRENT("..");
+               } else if ((node->inode->flags & FI_SIZED) == 0) {
+                               /* don't count duplicate names */
+                       node->inode->flags |= FI_SIZED;
+                       if (debug & DEBUG_FS_SIZE_DIR_NODE)
+                               printf("ffs_size_dir: `%s' size %lld\n",
+                                   node->name,
+                                   (long long)node->inode->st.st_size);
+                       fsopts->inodes++;
+                       if (node->type == S_IFREG)
+                               ADDSIZE(node->inode->st.st_size);
+                       if (node->type == S_IFLNK) {
+                               size_t  slen;
+
+                               slen = strlen(node->symlink) + 1;
+                               if (slen >= (ffs_opts->version == 1 ?
+                                               UFS1_MAXSYMLINKLEN :
+                                               UFS2_MAXSYMLINKLEN))
+                                       ADDSIZE(slen);
+                       }
+               }
+               if (node->type == S_IFDIR)
+                       ffs_size_dir(node->child, fsopts);
+       }
+       ADDSIZE(curdirsize);
+
+       if (debug & DEBUG_FS_SIZE_DIR)
+               printf("ffs_size_dir: exit: size %lld inodes %lld\n",
+                   (long long)fsopts->size, (long long)fsopts->inodes);
+}
+
+static void *
+ffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
+                fsnode *root, fsinfo_t *fsopts)
+{
+       size_t slen;
+       void *membuf;
+
+       memset(dinp, 0, sizeof(*dinp));
+       dinp->di_mode = cur->inode->st.st_mode;
+       dinp->di_nlink = cur->inode->nlink;
+       dinp->di_size = cur->inode->st.st_size;
+       dinp->di_atime = cur->inode->st.st_atime;
+       dinp->di_mtime = cur->inode->st.st_mtime;
+       dinp->di_ctime = cur->inode->st.st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+       dinp->di_atimensec = cur->inode->st.st_atimensec;
+       dinp->di_mtimensec = cur->inode->st.st_mtimensec;
+       dinp->di_ctimensec = cur->inode->st.st_ctimensec;
+#endif
+#if HAVE_STRUCT_STAT_ST_FLAGS
+       dinp->di_flags = cur->inode->st.st_flags;
+#endif
+#if HAVE_STRUCT_STAT_ST_GEN
+       dinp->di_gen = cur->inode->st.st_gen;
+#endif
+       dinp->di_uid = cur->inode->st.st_uid;
+       dinp->di_gid = cur->inode->st.st_gid;
+               /* not set: di_db, di_ib, di_blocks, di_spare */
+
+       membuf = NULL;
+       if (cur == root) {                      /* "."; write dirbuf */
+               membuf = dbufp->buf;
+               dinp->di_size = dbufp->size;
+       } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
+               dinp->di_size = 0;      /* a device */
+               dinp->di_rdev =
+                   ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap);
+       } else if (S_ISLNK(cur->type)) {        /* symlink */
+               slen = strlen(cur->symlink);
+               if (slen < UFS1_MAXSYMLINKLEN) {        /* short link */
+                       memcpy(dinp->di_db, cur->symlink, slen);
+               } else
+                       membuf = cur->symlink;
+               dinp->di_size = slen;
+       }
+       return membuf;
+}
+
+static void *
+ffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
+                fsnode *root, fsinfo_t *fsopts)
+{
+       size_t slen;
+       void *membuf;
+
+       memset(dinp, 0, sizeof(*dinp));
+       dinp->di_mode = cur->inode->st.st_mode;
+       dinp->di_nlink = cur->inode->nlink;
+       dinp->di_size = cur->inode->st.st_size;
+       dinp->di_atime = cur->inode->st.st_atime;
+       dinp->di_mtime = cur->inode->st.st_mtime;
+       dinp->di_ctime = cur->inode->st.st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+       dinp->di_atimensec = cur->inode->st.st_atimensec;
+       dinp->di_mtimensec = cur->inode->st.st_mtimensec;
+       dinp->di_ctimensec = cur->inode->st.st_ctimensec;
+#endif
+#if HAVE_STRUCT_STAT_ST_FLAGS
+       dinp->di_flags = cur->inode->st.st_flags;
+#endif
+#if HAVE_STRUCT_STAT_ST_GEN
+       dinp->di_gen = cur->inode->st.st_gen;
+#endif
+#if HAVE_STRUCT_STAT_BIRTHTIME
+       dinp->di_birthtime = cur->inode->st.st_birthtime;
+       dinp->di_birthnsec = cur->inode->st.st_birthtimensec;
+#endif
+       dinp->di_uid = cur->inode->st.st_uid;
+       dinp->di_gid = cur->inode->st.st_gid;
+               /* not set: di_db, di_ib, di_blocks, di_spare */
+
+       membuf = NULL;
+       if (cur == root) {                      /* "."; write dirbuf */
+               membuf = dbufp->buf;
+               dinp->di_size = dbufp->size;
+       } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
+               dinp->di_size = 0;      /* a device */
+               dinp->di_rdev =
+                   ufs_rw64(cur->inode->st.st_rdev, fsopts->needswap);
+       } else if (S_ISLNK(cur->type)) {        /* symlink */
+               slen = strlen(cur->symlink);
+               if (slen < UFS2_MAXSYMLINKLEN) {        /* short link */
+                       memcpy(dinp->di_db, cur->symlink, slen);
+               } else
+                       membuf = cur->symlink;
+               dinp->di_size = slen;
+       }
+       return membuf;
+}
+
+static int
+ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+       fsnode          *cur;
+       dirbuf_t        dirbuf;
+       union dinode    din;
+       void            *membuf;
+       char            path[MAXPATHLEN + 1];
+       ffs_opt_t       *ffs_opts = fsopts->fs_specific;
+
+       assert(dir != NULL);
+       assert(root != NULL);
+       assert(fsopts != NULL);
+       assert(ffs_opts != NULL);
+
+       (void)memset(&dirbuf, 0, sizeof(dirbuf));
+
+       if (debug & DEBUG_FS_POPULATE)
+               printf("ffs_populate_dir: PASS 1  dir %s node %p\n", dir, root);
+
+               /*
+                * pass 1: allocate inode numbers, build directory `file'
+                */
+       for (cur = root; cur != NULL; cur = cur->next) {
+               if ((cur->inode->flags & FI_ALLOCATED) == 0) {
+                       cur->inode->flags |= FI_ALLOCATED;
+                       if (cur == root && cur->parent != NULL)
+                               cur->inode->ino = cur->parent->inode->ino;
+                       else {
+                               cur->inode->ino = fsopts->curinode;
+                               fsopts->curinode++;
+                       }
+               }
+               ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap);
+               if (cur == root) {              /* we're at "."; add ".." */
+                       ffs_make_dirbuf(&dirbuf, "..",
+                           cur->parent == NULL ? cur : cur->parent->first,
+                           fsopts->needswap);
+                       root->inode->nlink++;   /* count my parent's link */
+               } else if (cur->child != NULL)
+                       root->inode->nlink++;   /* count my child's link */
+
+               /*
+                * XXX  possibly write file and long symlinks here,
+                *      ensuring that blocks get written before inodes?
+                *      otoh, this isn't a real filesystem, so who
+                *      cares about ordering? :-)
+                */
+       }
+       if (debug & DEBUG_FS_POPULATE_DIRBUF)
+               ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap);
+
+               /*
+                * pass 2: write out dirbuf, then non-directories at this level
+                */
+       if (debug & DEBUG_FS_POPULATE)
+               printf("ffs_populate_dir: PASS 2  dir %s\n", dir);
+       for (cur = root; cur != NULL; cur = cur->next) {
+               if (cur->inode->flags & FI_WRITTEN)
+                       continue;               /* skip hard-linked entries */
+               cur->inode->flags |= FI_WRITTEN;
+
+               if ((size_t)snprintf(path, sizeof(path), "%s/%s/%s", cur->root,
+                   cur->path, cur->name) >= sizeof(path))
+                       errx(1, "Pathname too long.");
+
+               if (cur->child != NULL)
+                       continue;               /* child creates own inode */
+
+                               /* build on-disk inode */
+               if (ffs_opts->version == 1)
+                       membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur,
+                           root, fsopts);
+               else
+                       membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur,
+                           root, fsopts);
+
+               if (debug & DEBUG_FS_POPULATE_NODE) {
+                       printf("ffs_populate_dir: writing ino %d, %s",
+                           cur->inode->ino, inode_type(cur->type));
+                       if (cur->inode->nlink > 1)
+                               printf(", nlink %d", cur->inode->nlink);
+                       putchar('\n');
+               }
+
+               if (membuf != NULL) {
+                       ffs_write_file(&din, cur->inode->ino, membuf, fsopts);
+               } else if (S_ISREG(cur->type)) {
+                       ffs_write_file(&din, cur->inode->ino, path, fsopts);
+               } else {
+                       assert (! S_ISDIR(cur->type));
+                       ffs_write_inode(&din, cur->inode->ino, fsopts);
+               }
+       }
+
+               /*
+                * pass 3: write out sub-directories
+                */
+       if (debug & DEBUG_FS_POPULATE)
+               printf("ffs_populate_dir: PASS 3  dir %s\n", dir);
+       for (cur = root; cur != NULL; cur = cur->next) {
+               if (cur->child == NULL)
+                       continue;
+               if ((size_t)snprintf(path, sizeof(path), "%s/%s", dir,
+                   cur->name) >= sizeof(path))
+                       errx(1, "Pathname too long.");
+               if (! ffs_populate_dir(path, cur->child, fsopts))
+                       return (0);
+       }
+
+       if (debug & DEBUG_FS_POPULATE)
+               printf("ffs_populate_dir: DONE dir %s\n", dir);
+
+               /* cleanup */
+       if (dirbuf.buf != NULL)
+               free(dirbuf.buf);
+       return (1);
+}
+
+
+static void
+ffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts)
+{
+       int     isfile, ffd;
+       char    *fbuf, *p;
+       off_t   bufleft, chunk, offset;
+       ssize_t nread;
+       struct inode    in;
+       struct buf *    bp;
+       ffs_opt_t       *ffs_opts = fsopts->fs_specific;
+       struct vnode vp = { fsopts, NULL };
+
+       assert (din != NULL);
+       assert (buf != NULL);
+       assert (fsopts != NULL);
+       assert (ffs_opts != NULL);
+
+       isfile = S_ISREG(DIP(din, mode));
+       fbuf = NULL;
+       ffd = -1;
+       p = NULL;
+
+       in.i_fs = (struct fs *)fsopts->superblock;
+       in.i_devvp = &vp;
+
+       if (debug & DEBUG_FS_WRITE_FILE) {
+               printf(
+                   "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld",
+                   ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT),
+                   (long long)DIP(din, size));
+               if (isfile)
+                       printf(", file '%s'\n", (char *)buf);
+               else
+                       printf(", buffer %p\n", buf);
+       }
+
+       in.i_number = ino;
+       in.i_size = DIP(din, size);
+       if (ffs_opts->version == 1)
+               memcpy(&in.i_din.ffs1_din, &din->ffs1_din,
+                   sizeof(in.i_din.ffs1_din));
+       else
+               memcpy(&in.i_din.ffs2_din, &din->ffs2_din,
+                   sizeof(in.i_din.ffs2_din));
+
+       if (DIP(din, size) == 0)
+               goto write_inode_and_leave;             /* mmm, cheating */
+
+       if (isfile) {
+               fbuf = emalloc(ffs_opts->bsize);
+               if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) {
+                       warn("Can't open `%s' for reading", (char *)buf);
+                       goto leave_ffs_write_file;
+               }
+       } else {
+               p = buf;
+       }
+
+       chunk = 0;
+       for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) {
+               chunk = MIN(bufleft, ffs_opts->bsize);
+               if (!isfile)
+                       ;
+               else if ((nread = read(ffd, fbuf, chunk)) == -1)
+                       err(EXIT_FAILURE, "Reading `%s', %lld bytes to go",
+                           (char *)buf, (long long)bufleft);
+               else if (nread != chunk)
+                       errx(EXIT_FAILURE, "Reading `%s', %lld bytes to go, "
+                           "read %zd bytes, expected %ju bytes, does "
+                           "metalog size= attribute mismatch source size?",
+                           (char *)buf, (long long)bufleft, nread,
+                           (uintmax_t)chunk);
+               else
+                       p = fbuf;
+               offset = DIP(din, size) - bufleft;
+               if (debug & DEBUG_FS_WRITE_FILE_BLOCK)
+                       printf(
+               "ffs_write_file: write %p offset %lld size %lld left %lld\n",
+                           p, (long long)offset,
+                           (long long)chunk, (long long)bufleft);
+       /*
+        * XXX  if holey support is desired, do the check here
+        *
+        * XXX  might need to write out last bit in fragroundup
+        *      sized chunk. however, ffs_balloc() handles this for us
+        */
+               errno = ffs_balloc(&in, offset, chunk, &bp);
+ bad_ffs_write_file:
+               if (errno != 0)
+                       err(1,
+                           "Writing inode %d (%s), bytes %lld + %lld",
+                           ino,
+                           isfile ? (char *)buf :
+                             inode_type(DIP(din, mode) & S_IFMT),
+                           (long long)offset, (long long)chunk);
+               memcpy(bp->b_data, p, chunk);
+               errno = bwrite(bp);
+               if (errno != 0)
+                       goto bad_ffs_write_file;
+               if (!isfile)
+                       p += chunk;
+       }
+  
+ write_inode_and_leave:
+       ffs_write_inode(&in.i_din, in.i_number, fsopts);
+
+ leave_ffs_write_file:
+       if (fbuf)
+               free(fbuf);
+       if (ffd != -1)
+               close(ffd);
+}
+
+
+static void
+ffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap)
+{
+       doff_t          i;
+       struct direct   *de;
+       uint16_t        reclen;
+
+       assert (dbuf != NULL);
+       assert (dir != NULL);
+       printf("ffs_dump_dirbuf: dir %s size %d cur %d\n",
+           dir, dbuf->size, dbuf->cur);
+
+       for (i = 0; i < dbuf->size; ) {
+               de = (struct direct *)(dbuf->buf + i);
+               reclen = ufs_rw16(de->d_reclen, needswap);
+               printf(
+           " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n",
+                   ufs_rw32(de->d_fileno, needswap),
+                   inode_type(DTTOIF(de->d_type)), i, reclen,
+                   de->d_namlen, de->d_name);
+               i += reclen;
+               assert(reclen > 0);
+       }
+}
+
+static void
+ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap)
+{
+       struct direct   de, *dp;
+       uint16_t        llen, reclen;
+       u_char          *newbuf;
+
+       assert (dbuf != NULL);
+       assert (name != NULL);
+       assert (node != NULL);
+                                       /* create direct entry */
+       (void)memset(&de, 0, sizeof(de));
+       de.d_fileno = ufs_rw32(node->inode->ino, needswap);
+       de.d_type = IFTODT(node->type);
+       de.d_namlen = (uint8_t)strlen(name);
+       strcpy(de.d_name, name);
+       reclen = UFS_DIRSIZ(0, &de, needswap);
+       de.d_reclen = ufs_rw16(reclen, needswap);
+
+       dp = (struct direct *)(dbuf->buf + dbuf->cur);
+       llen = 0;
+       if (dp != NULL)
+               llen = UFS_DIRSIZ(0, dp, needswap);
+
+       if (debug & DEBUG_FS_MAKE_DIRBUF)
+               printf(
+                   "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n"
+                   "  ino %d type %d reclen %d namlen %d name %.30s\n",
+                   dbuf->size, dbuf->cur, llen,
+                   ufs_rw32(de.d_fileno, needswap), de.d_type, reclen,
+                   de.d_namlen, de.d_name);
+
+       if (reclen + dbuf->cur + llen > roundup(dbuf->size, UFS_DIRBLKSIZ)) {
+               if (debug & DEBUG_FS_MAKE_DIRBUF)
+                       printf("ffs_make_dirbuf: growing buf to %d\n",
+                           dbuf->size + UFS_DIRBLKSIZ);
+               newbuf = erealloc(dbuf->buf, dbuf->size + UFS_DIRBLKSIZ);
+               dbuf->buf = newbuf;
+               dbuf->size += UFS_DIRBLKSIZ;
+               memset(dbuf->buf + dbuf->size - UFS_DIRBLKSIZ, 0, UFS_DIRBLKSIZ);
+               dbuf->cur = dbuf->size - UFS_DIRBLKSIZ;
+       } else if (dp) {                        /* shrink end of previous */
+               dp->d_reclen = ufs_rw16(llen,needswap);
+               dbuf->cur += llen;
+       }
+       dp = (struct direct *)(dbuf->buf + dbuf->cur);
+       memcpy(dp, &de, reclen);
+       dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap);
+}
+
+/*
+ * cribbed from sys/ufs/ffs/ffs_alloc.c
+ */
+static void
+ffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts)
+{
+       char            *buf;
+       struct ufs1_dinode *dp1;
+       struct ufs2_dinode *dp2, *dip;
+       struct cg       *cgp;
+       struct fs       *fs;
+       int             cg, cgino, i;
+       daddr_t         d;
+       char            sbbuf[FFS_MAXBSIZE];
+       uint32_t        initediblk;
+       ffs_opt_t       *ffs_opts = fsopts->fs_specific;
+
+       assert (dp != NULL);
+       assert (ino > 0);
+       assert (fsopts != NULL);
+       assert (ffs_opts != NULL);
+
+       fs = (struct fs *)fsopts->superblock;
+       cg = ino_to_cg(fs, ino);
+       cgino = ino % fs->fs_ipg;
+       if (debug & DEBUG_FS_WRITE_INODE)
+               printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n",
+                   dp, ino, cg, cgino);
+
+       ffs_rdfs(FFS_FSBTODB(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
+           fsopts);
+       cgp = (struct cg *)sbbuf;
+       if (!cg_chkmagic(cgp, fsopts->needswap))
+               errx(1, "ffs_write_inode: cg %d: bad magic number", cg);
+
+       assert (isclr(cg_inosused(cgp, fsopts->needswap), cgino));
+
+       buf = emalloc(fs->fs_bsize);
+       dp1 = (struct ufs1_dinode *)buf;
+       dp2 = (struct ufs2_dinode *)buf;
+
+       if (fs->fs_cstotal.cs_nifree == 0)
+               errx(1, "ffs_write_inode: fs out of inodes for ino %u",
+                   ino);
+       if (fs->fs_cs(fs, cg).cs_nifree == 0)
+               errx(1,
+                   "ffs_write_inode: cg %d out of inodes for ino %u",
+                   cg, ino);
+       setbit(cg_inosused(cgp, fsopts->needswap), cgino);
+       ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap);
+       fs->fs_cstotal.cs_nifree--;
+       fs->fs_cs(fs, cg).cs_nifree--;
+       if (S_ISDIR(DIP(dp, mode))) {
+               ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap);
+               fs->fs_cstotal.cs_ndir++;
+               fs->fs_cs(fs, cg).cs_ndir++; 
+       }
+
+       /*
+        * Initialize inode blocks on the fly for UFS2.
+        */
+       initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap);
+       if (ffs_opts->version == 2 &&
+           (uint32_t)(cgino + FFS_INOPB(fs)) > initediblk &&
+           initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) {
+               memset(buf, 0, fs->fs_bsize);
+               dip = (struct ufs2_dinode *)buf;
+               srandom(time(NULL));
+               for (i = 0; i < FFS_INOPB(fs); i++) {
+                       dip->di_gen = random() / 2 + 1;
+                       dip++;
+               }
+               ffs_wtfs(FFS_FSBTODB(fs, ino_to_fsba(fs,
+                                 cg * fs->fs_ipg + initediblk)),
+                   fs->fs_bsize, buf, fsopts);
+               initediblk += FFS_INOPB(fs);
+               cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap);
+       }
+
+
+       ffs_wtfs(FFS_FSBTODB(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
+           fsopts);
+
+                                       /* now write inode */
+       d = FFS_FSBTODB(fs, ino_to_fsba(fs, ino));
+       ffs_rdfs(d, fs->fs_bsize, buf, fsopts);
+       if (fsopts->needswap) {
+               if (ffs_opts->version == 1)
+                       ffs_dinode1_swap(&dp->ffs1_din,
+                           &dp1[ino_to_fsbo(fs, ino)]);
+               else
+                       ffs_dinode2_swap(&dp->ffs2_din,
+                           &dp2[ino_to_fsbo(fs, ino)]);
+       } else {
+               if (ffs_opts->version == 1)
+                       dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din;
+               else
+                       dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din;
+       }
+       ffs_wtfs(d, fs->fs_bsize, buf, fsopts);
+       free(buf);
+}
+
+void
+panic(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       vwarnx(fmt, ap);
+       va_end(ap);
+       exit(1);
+}
diff --git a/usr.sbin/makefs/ffs.h b/usr.sbin/makefs/ffs.h
new file mode 100644 (file)
index 0000000..79223bd
--- /dev/null
@@ -0,0 +1,68 @@
+/*     $NetBSD: ffs.h,v 1.2 2011/10/09 21:33:43 christos Exp $ */
+
+/*
+ * Copyright (c) 2001-2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#ifndef _FFS_H
+#define _FFS_H
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+typedef struct {
+       char    label[MAXVOLLEN];       /* volume name/label */
+       int     bsize;          /* block size */
+       int     fsize;          /* fragment size */
+       int     cpg;            /* cylinders per group */
+       int     cpgflg;         /* cpg was specified by user */
+       int     density;        /* bytes per inode */
+       int     ntracks;        /* number of tracks */
+       int     nsectors;       /* number of sectors */
+       int     rpm;            /* rpm */
+       int     minfree;        /* free space threshold */
+       int     optimization;   /* optimization (space or time) */
+       int     maxcontig;      /* max contiguous blocks to allocate */
+       int     rotdelay;       /* rotational delay between blocks */
+       int     maxbpg;         /* maximum blocks per file in a cyl group */
+       int     nrpos;          /* # of distinguished rotational positions */
+       int     avgfilesize;    /* expected average file size */
+       int     avgfpdir;       /* expected # of files per directory */
+       int     version;        /* filesystem version (1 = FFS, 2 = UFS2) */
+       int     maxbsize;       /* maximum extent size */
+       int     maxblkspercg;   /* max # of blocks per cylinder group */
+               /* XXX: support `old' file systems ? */
+} ffs_opt_t;
+
+#endif /* _FFS_H */
diff --git a/usr.sbin/makefs/ffs/Makefile.inc b/usr.sbin/makefs/ffs/Makefile.inc
new file mode 100644 (file)
index 0000000..d13bcb3
--- /dev/null
@@ -0,0 +1,7 @@
+#      $NetBSD: Makefile.inc,v 1.1 2009/01/16 19:39:52 pooka Exp $
+#
+
+.PATH: ${.CURDIR}/ffs ${NETBSDSRCDIR}/sys/ufs/ffs
+
+SRCS+= ffs_alloc.c ffs_balloc.c ffs_bswap.c ffs_subr.c ffs_tables.c ufs_bmap.c
+SRCS+= buf.c mkfs.c
diff --git a/usr.sbin/makefs/ffs/buf.c b/usr.sbin/makefs/ffs/buf.c
new file mode 100644 (file)
index 0000000..1917c3a
--- /dev/null
@@ -0,0 +1,220 @@
+/*     $NetBSD: buf.c,v 1.21 2013/02/03 03:21:21 christos Exp $        */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: buf.c,v 1.21 2013/02/03 03:21:21 christos Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "buf.h"
+
+TAILQ_HEAD(buftailhead,buf) buftail;
+
+int
+bread(struct vnode *vp, daddr_t blkno, int size, struct kauth_cred *u1 __unused,
+   int u2 __unused, struct buf **bpp)
+{
+       off_t   offset;
+       ssize_t rv;
+       fsinfo_t *fs = vp->fs;
+
+       assert (bpp != NULL);
+
+       if (debug & DEBUG_BUF_BREAD)
+               printf("bread: blkno %lld size %d\n", (long long)blkno, size);
+       *bpp = getblk(vp, blkno, size, 0, 0);
+       offset = (*bpp)->b_blkno * fs->sectorsize + fs->offset;
+       if (debug & DEBUG_BUF_BREAD)
+               printf("bread: blkno %lld offset %lld bcount %ld\n",
+                   (long long)(*bpp)->b_blkno, (long long) offset,
+                   (*bpp)->b_bcount);
+       if (lseek((*bpp)->b_fs->fd, offset, SEEK_SET) == -1)
+               err(1, "%s: lseek %lld (%lld)", __func__,
+                   (long long)(*bpp)->b_blkno, (long long)offset);
+       rv = read((*bpp)->b_fs->fd, (*bpp)->b_data, (*bpp)->b_bcount);
+       if (debug & DEBUG_BUF_BREAD)
+               printf("bread: read %ld (%lld) returned %zd\n",
+                   (*bpp)->b_bcount, (long long)offset, rv);
+       if (rv == -1)                           /* read error */
+               err(1, "%s: read %ld (%lld) returned %zd", __func__,
+                   (*bpp)->b_bcount, (long long)offset, rv);
+       else if (rv != (*bpp)->b_bcount)        /* short read */
+               err(1, "%s: read %ld (%lld) returned %zd", __func__,
+                   (*bpp)->b_bcount, (long long)offset, rv);
+       else
+               return (0);
+}
+
+void
+brelse(struct buf *bp, int u1 __unused)
+{
+
+       assert (bp != NULL);
+       assert (bp->b_data != NULL);
+
+       if (bp->b_lblkno < 0) {
+               /*
+                * XXX  don't remove any buffers with negative logical block
+                *      numbers (lblkno), so that we retain the mapping
+                *      of negative lblkno -> real blkno that ffs_balloc()
+                *      sets up.
+                *
+                *      if we instead released these buffers, and implemented
+                *      ufs_strategy() (and ufs_bmaparray()) and called those
+                *      from bread() and bwrite() to convert the lblkno to
+                *      a real blkno, we'd add a lot more code & complexity
+                *      and reading off disk, for little gain, because this
+                *      simple hack works for our purpose.
+                */
+               bp->b_bcount = 0;
+               return;
+       }
+               
+       TAILQ_REMOVE(&buftail, bp, b_tailq);
+       free(bp->b_data);
+       free(bp);
+}
+
+int
+bwrite(struct buf *bp)
+{
+       off_t   offset;
+       ssize_t rv;
+       int     bytes;
+       fsinfo_t *fs = bp->b_fs;
+
+       assert (bp != NULL);
+       offset = bp->b_blkno * fs->sectorsize + fs->offset;
+       bytes  = bp->b_bcount;
+       if (debug & DEBUG_BUF_BWRITE)
+               printf("bwrite: blkno %lld offset %lld bcount %d\n",
+                   (long long)bp->b_blkno, (long long) offset, bytes);
+       if (lseek(bp->b_fs->fd, offset, SEEK_SET) == -1)
+               return (errno);
+       rv = write(bp->b_fs->fd, bp->b_data, bytes);
+       if (debug & DEBUG_BUF_BWRITE)
+               printf("bwrite: write %ld (offset %lld) returned %lld\n",
+                   bp->b_bcount, (long long)offset, (long long)rv);
+       brelse(bp, 0);
+       if (rv == bytes)
+               return (0);
+       else if (rv == -1)              /* write error */
+               return (errno);
+       else                            /* short write ? */
+               return (EAGAIN);
+}
+
+void
+bcleanup(void)
+{
+       struct buf *bp;
+
+       /*
+        * XXX  this really shouldn't be necessary, but i'm curious to
+        *      know why there's still some buffers lying around that
+        *      aren't brelse()d
+        */
+       
+       if (TAILQ_EMPTY(&buftail))
+               return;
+
+       printf("bcleanup: unflushed buffers:\n");
+       TAILQ_FOREACH(bp, &buftail, b_tailq) {
+               printf("\tlblkno %10lld  blkno %10lld  count %6ld  bufsize %6ld\n",
+                   (long long)bp->b_lblkno, (long long)bp->b_blkno,
+                   bp->b_bcount, bp->b_bufsize);
+       }
+       printf("bcleanup: done\n");
+}
+
+struct buf *
+getblk(struct vnode *vp, daddr_t blkno, int size, int u1 __unused,
+    int u2 __unused)
+{
+       static int buftailinitted;
+       struct buf *bp;
+       void *n;
+
+       if (debug & DEBUG_BUF_GETBLK)
+               printf("getblk: blkno %lld size %d\n", (long long)blkno, size);
+
+       bp = NULL;
+       if (!buftailinitted) {
+               if (debug & DEBUG_BUF_GETBLK)
+                       printf("getblk: initialising tailq\n");
+               TAILQ_INIT(&buftail);
+               buftailinitted = 1;
+       } else {
+               TAILQ_FOREACH(bp, &buftail, b_tailq) {
+                       if (bp->b_lblkno != blkno)
+                               continue;
+                       break;
+               }
+       }
+       if (bp == NULL) {
+               bp = ecalloc(1, sizeof(*bp));
+               bp->b_bufsize = 0;
+               bp->b_blkno = bp->b_lblkno = blkno;
+               bp->b_fs = vp->fs;
+               bp->b_data = NULL;
+               TAILQ_INSERT_HEAD(&buftail, bp, b_tailq);
+       }
+       bp->b_bcount = size;
+       if (bp->b_data == NULL || bp->b_bcount > bp->b_bufsize) {
+               n = erealloc(bp->b_data, size);
+               memset(n, 0, size);
+               bp->b_data = n;
+               bp->b_bufsize = size;
+       }
+
+       return (bp);
+}
diff --git a/usr.sbin/makefs/ffs/buf.h b/usr.sbin/makefs/ffs/buf.h
new file mode 100644 (file)
index 0000000..8fd6893
--- /dev/null
@@ -0,0 +1,117 @@
+/*     $NetBSD: buf.h,v 1.9 2013/01/30 19:19:19 christos Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#ifndef _FFS_BUF_H
+#define        _FFS_BUF_H
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <err.h>
+
+struct componentname {
+       char *cn_nameptr;
+       size_t cn_namelen;
+};
+
+struct makefs_fsinfo;
+struct vnode {
+       struct makefs_fsinfo *fs;
+       void *v_data;
+};
+
+#define vput(a) ((void)(a))
+
+struct buf {
+       void *          b_data;
+       long            b_bufsize;
+       long            b_bcount;
+       daddr_t         b_blkno;
+       daddr_t         b_lblkno;
+       struct makefs_fsinfo * b_fs;
+
+       TAILQ_ENTRY(buf)        b_tailq;
+};
+
+struct kauth_cred;
+void           bcleanup(void);
+int            bread(struct vnode *, daddr_t, int, struct kauth_cred *,
+    int, struct buf **);
+void           brelse(struct buf *, int);
+int            bwrite(struct buf *);
+struct buf *   getblk(struct vnode *, daddr_t, int, int, int);
+
+#define        bdwrite(bp)     bwrite(bp)
+#define        clrbuf(bp)      memset((bp)->b_data, 0, (u_int)(bp)->b_bcount)
+
+#define        B_MODIFY        0
+#define        BC_AGE          0
+
+#define min(a, b) MIN((a), (b))
+#define microtime(tv) gettimeofday((tv), NULL)
+#define KASSERT(a)
+#define IO_SYNC        1
+
+struct pool {
+       size_t size;
+};
+
+#define pool_init(p, s, a1, a2, a3, a4, a5, a6)        (p)->size = (s)
+#define pool_get(p, f) ecalloc(1, (p)->size)
+#define pool_put(p, a) free(a)
+#define pool_destroy(p)
+
+#define MALLOC_DECLARE(a)
+#define malloc_type_attach(a)
+#define malloc_type_detach(a)
+
+#define mutex_enter(m)
+#define mutex_exit(m)
+#define mutex_init(m, t, i)
+#define mutex_destroy(m)
+
+#define desiredvnodes 10000
+#define NOCRED NULL
+#define DEV_BSHIFT 9
+
+#endif /* _FFS_BUF_H */
diff --git a/usr.sbin/makefs/ffs/ffs_alloc.c b/usr.sbin/makefs/ffs/ffs_alloc.c
new file mode 100644 (file)
index 0000000..63ab463
--- /dev/null
@@ -0,0 +1,597 @@
+/*     $NetBSD: ffs_alloc.c,v 1.27 2013/06/23 22:03:34 dholland Exp $  */
+/* From: NetBSD: ffs_alloc.c,v 1.50 2001/09/06 02:16:01 lukem Exp */
+
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program
+ *
+ * Copyright (c) 1982, 1986, 1989, 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.
+ *
+ *     @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: ffs_alloc.c,v 1.27 2013/06/23 22:03:34 dholland Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <errno.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+
+static int scanc(u_int, const u_char *, const u_char *, int);
+
+static daddr_t ffs_alloccg(struct inode *, int, daddr_t, int);
+static daddr_t ffs_alloccgblk(struct inode *, struct buf *, daddr_t);
+static daddr_t ffs_hashalloc(struct inode *, int, daddr_t, int,
+                    daddr_t (*)(struct inode *, int, daddr_t, int));
+static int32_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int);
+
+/* in ffs_tables.c */
+extern const int inside[], around[];
+extern const u_char * const fragtbl[];
+
+/*
+ * Allocate a block in the file system.
+ * 
+ * The size of the requested block is given, which must be some
+ * multiple of fs_fsize and <= fs_bsize.
+ * A preference may be optionally specified. If a preference is given
+ * the following hierarchy is used to allocate a block:
+ *   1) allocate the requested block.
+ *   2) allocate a rotationally optimal block in the same cylinder.
+ *   3) allocate a block in the same cylinder group.
+ *   4) quadradically rehash into other cylinder groups, until an
+ *      available block is located.
+ * If no block preference is given the following hierarchy is used
+ * to allocate a block:
+ *   1) allocate a block in the cylinder group that contains the
+ *      inode for the file.
+ *   2) quadradically rehash into other cylinder groups, until an
+ *      available block is located.
+ */
+int
+ffs_alloc(struct inode *ip, daddr_t lbn __unused, daddr_t bpref, int size,
+    daddr_t *bnp)
+{
+       struct fs *fs = ip->i_fs;
+       daddr_t bno;
+       int cg;
+       
+       *bnp = 0;
+       if (size > fs->fs_bsize || ffs_fragoff(fs, size) != 0) {
+               errx(1, "ffs_alloc: bad size: bsize %d size %d",
+                   fs->fs_bsize, size);
+       }
+       if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0)
+               goto nospace;
+       if (bpref >= fs->fs_size)
+               bpref = 0;
+       if (bpref == 0)
+               cg = ino_to_cg(fs, ip->i_number);
+       else
+               cg = dtog(fs, bpref);
+       bno = ffs_hashalloc(ip, cg, bpref, size, ffs_alloccg);
+       if (bno > 0) {
+               DIP_ADD(ip, blocks, size / DEV_BSIZE);
+               *bnp = bno;
+               return (0);
+       }
+nospace:
+       return (ENOSPC);
+}
+
+/*
+ * Select the desired position for the next block in a file.  The file is
+ * logically divided into sections. The first section is composed of the
+ * direct blocks. Each additional section contains fs_maxbpg blocks.
+ * 
+ * If no blocks have been allocated in the first section, the policy is to
+ * request a block in the same cylinder group as the inode that describes
+ * the file. If no blocks have been allocated in any other section, the
+ * policy is to place the section in a cylinder group with a greater than
+ * average number of free blocks.  An appropriate cylinder group is found
+ * by using a rotor that sweeps the cylinder groups. When a new group of
+ * blocks is needed, the sweep begins in the cylinder group following the
+ * cylinder group from which the previous allocation was made. The sweep
+ * continues until a cylinder group with greater than the average number
+ * of free blocks is found. If the allocation is for the first block in an
+ * indirect block, the information on the previous allocation is unavailable;
+ * here a best guess is made based upon the logical block number being
+ * allocated.
+ * 
+ * If a section is already partially allocated, the policy is to
+ * contiguously allocate fs_maxcontig blocks.  The end of one of these
+ * contiguous blocks and the beginning of the next is physically separated
+ * so that the disk head will be in transit between them for at least
+ * fs_rotdelay milliseconds.  This is to allow time for the processor to
+ * schedule another I/O transfer.
+ */
+/* XXX ondisk32 */
+daddr_t
+ffs_blkpref_ufs1(struct inode *ip, daddr_t lbn, int indx, int32_t *bap)
+{
+       struct fs *fs;
+       int cg;
+       int avgbfree, startcg;
+
+       fs = ip->i_fs;
+       if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+               if (lbn < UFS_NDADDR + FFS_NINDIR(fs)) {
+                       cg = ino_to_cg(fs, ip->i_number);
+                       return (fs->fs_fpg * cg + fs->fs_frag);
+               }
+               /*
+                * Find a cylinder with greater than average number of
+                * unused data blocks.
+                */
+               if (indx == 0 || bap[indx - 1] == 0)
+                       startcg =
+                           ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg;
+               else
+                       startcg = dtog(fs,
+                               ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1);
+               startcg %= fs->fs_ncg;
+               avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
+               for (cg = startcg; cg < fs->fs_ncg; cg++)
+                       if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree)
+                               return (fs->fs_fpg * cg + fs->fs_frag);
+               for (cg = 0; cg <= startcg; cg++)
+                       if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree)
+                               return (fs->fs_fpg * cg + fs->fs_frag);
+               return (0);
+       }
+       /*
+        * We just always try to lay things out contiguously.
+        */
+       return ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag;
+}
+
+daddr_t
+ffs_blkpref_ufs2(struct inode *ip, daddr_t lbn, int indx, int64_t *bap)
+{
+       struct fs *fs;
+       int cg;
+       int avgbfree, startcg;
+
+       fs = ip->i_fs;
+       if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+               if (lbn < UFS_NDADDR + FFS_NINDIR(fs)) {
+                       cg = ino_to_cg(fs, ip->i_number);
+                       return (fs->fs_fpg * cg + fs->fs_frag);
+               }
+               /*
+                * Find a cylinder with greater than average number of
+                * unused data blocks.
+                */
+               if (indx == 0 || bap[indx - 1] == 0)
+                       startcg =
+                           ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg;
+               else
+                       startcg = dtog(fs,
+                               ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1);
+               startcg %= fs->fs_ncg;
+               avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
+               for (cg = startcg; cg < fs->fs_ncg; cg++)
+                       if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
+                               return (fs->fs_fpg * cg + fs->fs_frag);
+                       }
+               for (cg = 0; cg < startcg; cg++)
+                       if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
+                               return (fs->fs_fpg * cg + fs->fs_frag);
+                       }
+               return (0);
+       }
+       /*
+        * We just always try to lay things out contiguously.
+        */
+       return ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag;
+}
+
+/*
+ * Implement the cylinder overflow algorithm.
+ *
+ * The policy implemented by this algorithm is:
+ *   1) allocate the block in its requested cylinder group.
+ *   2) quadradically rehash on the cylinder group number.
+ *   3) brute force search for a free block.
+ *
+ * `size':     size for data blocks, mode for inodes
+ */
+/*VARARGS5*/
+static daddr_t
+ffs_hashalloc(struct inode *ip, int cg, daddr_t pref, int size,
+    daddr_t (*allocator)(struct inode *, int, daddr_t, int))
+{
+       struct fs *fs;
+       daddr_t result;
+       int i, icg = cg;
+
+       fs = ip->i_fs;
+       /*
+        * 1: preferred cylinder group
+        */
+       result = (*allocator)(ip, cg, pref, size);
+       if (result)
+               return (result);
+       /*
+        * 2: quadratic rehash
+        */
+       for (i = 1; i < fs->fs_ncg; i *= 2) {
+               cg += i;
+               if (cg >= fs->fs_ncg)
+                       cg -= fs->fs_ncg;
+               result = (*allocator)(ip, cg, 0, size);
+               if (result)
+                       return (result);
+       }
+       /*
+        * 3: brute force search
+        * Note that we start at i == 2, since 0 was checked initially,
+        * and 1 is always checked in the quadratic rehash.
+        */
+       cg = (icg + 2) % fs->fs_ncg;
+       for (i = 2; i < fs->fs_ncg; i++) {
+               result = (*allocator)(ip, cg, 0, size);
+               if (result)
+                       return (result);
+               cg++;
+               if (cg == fs->fs_ncg)
+                       cg = 0;
+       }
+       return (0);
+}
+
+/*
+ * Determine whether a block can be allocated.
+ *
+ * Check to see if a block of the appropriate size is available,
+ * and if it is, allocate it.
+ */
+static daddr_t
+ffs_alloccg(struct inode *ip, int cg, daddr_t bpref, int size)
+{
+       struct cg *cgp;
+       struct buf *bp;
+       daddr_t bno, blkno;
+       int error, frags, allocsiz, i;
+       struct fs *fs = ip->i_fs;
+       const int needswap = UFS_FSNEEDSWAP(fs);
+
+       if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize)
+               return (0);
+       error = bread(ip->i_devvp, FFS_FSBTODB(fs, cgtod(fs, cg)),
+           (int)fs->fs_cgsize, NULL, 0, &bp);
+       if (error) {
+               return (0);
+       }
+       cgp = (struct cg *)bp->b_data;
+       if (!cg_chkmagic(cgp, needswap) ||
+           (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) {
+               brelse(bp, 0);
+               return (0);
+       }
+       if (size == fs->fs_bsize) {
+               bno = ffs_alloccgblk(ip, bp, bpref);
+               bwrite(bp);
+               return (bno);
+       }
+       /*
+        * check to see if any fragments are already available
+        * allocsiz is the size which will be allocated, hacking
+        * it down to a smaller size if necessary
+        */
+       frags = ffs_numfrags(fs, size);
+       for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++)
+               if (cgp->cg_frsum[allocsiz] != 0)
+                       break;
+       if (allocsiz == fs->fs_frag) {
+               /*
+                * no fragments were available, so a block will be 
+                * allocated, and hacked up
+                */
+               if (cgp->cg_cs.cs_nbfree == 0) {
+                       brelse(bp, 0);
+                       return (0);
+               }
+               bno = ffs_alloccgblk(ip, bp, bpref);
+               bpref = dtogd(fs, bno);
+               for (i = frags; i < fs->fs_frag; i++)
+                       setbit(cg_blksfree(cgp, needswap), bpref + i);
+               i = fs->fs_frag - frags;
+               ufs_add32(cgp->cg_cs.cs_nffree, i, needswap);
+               fs->fs_cstotal.cs_nffree += i;
+               fs->fs_cs(fs, cg).cs_nffree += i;
+               fs->fs_fmod = 1;
+               ufs_add32(cgp->cg_frsum[i], 1, needswap);
+               bdwrite(bp);
+               return (bno);
+       }
+       bno = ffs_mapsearch(fs, cgp, bpref, allocsiz);
+       for (i = 0; i < frags; i++)
+               clrbit(cg_blksfree(cgp, needswap), bno + i);
+       ufs_add32(cgp->cg_cs.cs_nffree, -frags, needswap);
+       fs->fs_cstotal.cs_nffree -= frags;
+       fs->fs_cs(fs, cg).cs_nffree -= frags;
+       fs->fs_fmod = 1;
+       ufs_add32(cgp->cg_frsum[allocsiz], -1, needswap);
+       if (frags != allocsiz)
+               ufs_add32(cgp->cg_frsum[allocsiz - frags], 1, needswap);
+       blkno = cg * fs->fs_fpg + bno;
+       bdwrite(bp);
+       return blkno;
+}
+
+/*
+ * Allocate a block in a cylinder group.
+ *
+ * This algorithm implements the following policy:
+ *   1) allocate the requested block.
+ *   2) allocate a rotationally optimal block in the same cylinder.
+ *   3) allocate the next available block on the block rotor for the
+ *      specified cylinder group.
+ * Note that this routine only allocates fs_bsize blocks; these
+ * blocks may be fragmented by the routine that allocates them.
+ */
+static daddr_t
+ffs_alloccgblk(struct inode *ip, struct buf *bp, daddr_t bpref)
+{
+       struct cg *cgp;
+       daddr_t blkno;
+       int32_t bno;
+       struct fs *fs = ip->i_fs;
+       const int needswap = UFS_FSNEEDSWAP(fs);
+       u_int8_t *blksfree;
+
+       cgp = (struct cg *)bp->b_data;
+       blksfree = cg_blksfree(cgp, needswap);
+       if (bpref == 0 || dtog(fs, bpref) != ufs_rw32(cgp->cg_cgx, needswap)) {
+               bpref = ufs_rw32(cgp->cg_rotor, needswap);
+       } else {
+               bpref = ffs_blknum(fs, bpref);
+               bno = dtogd(fs, bpref);
+               /*
+                * if the requested block is available, use it
+                */
+               if (ffs_isblock(fs, blksfree, ffs_fragstoblks(fs, bno)))
+                       goto gotit;
+       }
+       /*
+        * Take the next available one in this cylinder group.
+        */
+       bno = ffs_mapsearch(fs, cgp, bpref, (int)fs->fs_frag);
+       if (bno < 0)
+               return (0);
+       cgp->cg_rotor = ufs_rw32(bno, needswap);
+gotit:
+       blkno = ffs_fragstoblks(fs, bno);
+       ffs_clrblock(fs, blksfree, (long)blkno);
+       ffs_clusteracct(fs, cgp, blkno, -1);
+       ufs_add32(cgp->cg_cs.cs_nbfree, -1, needswap);
+       fs->fs_cstotal.cs_nbfree--;
+       fs->fs_cs(fs, ufs_rw32(cgp->cg_cgx, needswap)).cs_nbfree--;
+       fs->fs_fmod = 1;
+       blkno = ufs_rw32(cgp->cg_cgx, needswap) * fs->fs_fpg + bno;
+       return (blkno);
+}
+
+/*
+ * Free a block or fragment.
+ *
+ * The specified block or fragment is placed back in the
+ * free map. If a fragment is deallocated, a possible 
+ * block reassembly is checked.
+ */
+void
+ffs_blkfree(struct inode *ip, daddr_t bno, long size)
+{
+       struct cg *cgp;
+       struct buf *bp;
+       int32_t fragno, cgbno;
+       int i, error, cg, blk, frags, bbase;
+       struct fs *fs = ip->i_fs;
+       const int needswap = UFS_FSNEEDSWAP(fs);
+
+       if (size > fs->fs_bsize || ffs_fragoff(fs, size) != 0 ||
+           ffs_fragnum(fs, bno) + ffs_numfrags(fs, size) > fs->fs_frag) {
+               errx(1, "blkfree: bad size: bno %lld bsize %d size %ld",
+                   (long long)bno, fs->fs_bsize, size);
+       }
+       cg = dtog(fs, bno);
+       if (bno >= fs->fs_size) {
+               warnx("bad block %lld, ino %llu", (long long)bno,
+                   (unsigned long long)ip->i_number);
+               return;
+       }
+       error = bread(ip->i_devvp, FFS_FSBTODB(fs, cgtod(fs, cg)),
+           (int)fs->fs_cgsize, NULL, 0, &bp);
+       if (error) {
+               brelse(bp, 0);
+               return;
+       }
+       cgp = (struct cg *)bp->b_data;
+       if (!cg_chkmagic(cgp, needswap)) {
+               brelse(bp, 0);
+               return;
+       }
+       cgbno = dtogd(fs, bno);
+       if (size == fs->fs_bsize) {
+               fragno = ffs_fragstoblks(fs, cgbno);
+               if (!ffs_isfreeblock(fs, cg_blksfree(cgp, needswap), fragno)) {
+                       errx(1, "blkfree: freeing free block %lld",
+                           (long long)bno);
+               }
+               ffs_setblock(fs, cg_blksfree(cgp, needswap), fragno);
+               ffs_clusteracct(fs, cgp, fragno, 1);
+               ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap);
+               fs->fs_cstotal.cs_nbfree++;
+               fs->fs_cs(fs, cg).cs_nbfree++;
+       } else {
+               bbase = cgbno - ffs_fragnum(fs, cgbno);
+               /*
+                * decrement the counts associated with the old frags
+                */
+               blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase);
+               ffs_fragacct(fs, blk, cgp->cg_frsum, -1, needswap);
+               /*
+                * deallocate the fragment
+                */
+               frags = ffs_numfrags(fs, size);
+               for (i = 0; i < frags; i++) {
+                       if (isset(cg_blksfree(cgp, needswap), cgbno + i)) {
+                               errx(1, "blkfree: freeing free frag: block %lld",
+                                   (long long)(cgbno + i));
+                       }
+                       setbit(cg_blksfree(cgp, needswap), cgbno + i);
+               }
+               ufs_add32(cgp->cg_cs.cs_nffree, i, needswap);
+               fs->fs_cstotal.cs_nffree += i;
+               fs->fs_cs(fs, cg).cs_nffree += i;
+               /*
+                * add back in counts associated with the new frags
+                */
+               blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase);
+               ffs_fragacct(fs, blk, cgp->cg_frsum, 1, needswap);
+               /*
+                * if a complete block has been reassembled, account for it
+                */
+               fragno = ffs_fragstoblks(fs, bbase);
+               if (ffs_isblock(fs, cg_blksfree(cgp, needswap), fragno)) {
+                       ufs_add32(cgp->cg_cs.cs_nffree, -fs->fs_frag, needswap);
+                       fs->fs_cstotal.cs_nffree -= fs->fs_frag;
+                       fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag;
+                       ffs_clusteracct(fs, cgp, fragno, 1);
+                       ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap);
+                       fs->fs_cstotal.cs_nbfree++;
+                       fs->fs_cs(fs, cg).cs_nbfree++;
+               }
+       }
+       fs->fs_fmod = 1;
+       bdwrite(bp);
+}
+
+
+static int
+scanc(u_int size, const u_char *cp, const u_char table[], int mask)
+{
+       const u_char *end = &cp[size];
+
+       while (cp < end && (table[*cp] & mask) == 0)
+               cp++;
+       return (end - cp);
+}
+
+/*
+ * Find a block of the specified size in the specified cylinder group.
+ *
+ * It is a panic if a request is made to find a block if none are
+ * available.
+ */
+static int32_t
+ffs_mapsearch(struct fs *fs, struct cg *cgp, daddr_t bpref, int allocsiz)
+{
+       int32_t bno;
+       int start, len, loc, i;
+       int blk, field, subfield, pos;
+       int ostart, olen;
+       const int needswap = UFS_FSNEEDSWAP(fs);
+
+       /*
+        * find the fragment by searching through the free block
+        * map for an appropriate bit pattern
+        */
+       if (bpref)
+               start = dtogd(fs, bpref) / NBBY;
+       else
+               start = ufs_rw32(cgp->cg_frotor, needswap) / NBBY;
+       len = howmany(fs->fs_fpg, NBBY) - start;
+       ostart = start;
+       olen = len;
+       loc = scanc((u_int)len,
+               (const u_char *)&cg_blksfree(cgp, needswap)[start],
+               (const u_char *)fragtbl[fs->fs_frag],
+               (1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+       if (loc == 0) {
+               len = start + 1;
+               start = 0;
+               loc = scanc((u_int)len,
+                       (const u_char *)&cg_blksfree(cgp, needswap)[0],
+                       (const u_char *)fragtbl[fs->fs_frag],
+                       (1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+               if (loc == 0) {
+                       errx(1,
+    "ffs_alloccg: map corrupted: start %d len %d offset %d %ld",
+                               ostart, olen,
+                               ufs_rw32(cgp->cg_freeoff, needswap),
+                               (long)cg_blksfree(cgp, needswap) - (long)cgp);
+                       /* NOTREACHED */
+               }
+       }
+       bno = (start + len - loc) * NBBY;
+       cgp->cg_frotor = ufs_rw32(bno, needswap);
+       /*
+        * found the byte in the map
+        * sift through the bits to find the selected frag
+        */
+       for (i = bno + NBBY; bno < i; bno += fs->fs_frag) {
+               blk = blkmap(fs, cg_blksfree(cgp, needswap), bno);
+               blk <<= 1;
+               field = around[allocsiz];
+               subfield = inside[allocsiz];
+               for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) {
+                       if ((blk & field) == subfield)
+                               return (bno + pos);
+                       field <<= 1;
+                       subfield <<= 1;
+               }
+       }
+       errx(1, "ffs_alloccg: block not in map: bno %lld", (long long)bno);
+       return (-1);
+}
diff --git a/usr.sbin/makefs/ffs/ffs_balloc.c b/usr.sbin/makefs/ffs/ffs_balloc.c
new file mode 100644 (file)
index 0000000..e5dd1d3
--- /dev/null
@@ -0,0 +1,584 @@
+/*     $NetBSD: ffs_balloc.c,v 1.20 2013/06/23 07:28:37 dholland Exp $ */
+/* From NetBSD: ffs_balloc.c,v 1.25 2001/08/08 08:36:36 lukem Exp */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 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.
+ *
+ *     @(#)ffs_balloc.c        8.8 (Berkeley) 6/16/95
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: ffs_balloc.c,v 1.20 2013/06/23 07:28:37 dholland Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+static int ffs_balloc_ufs1(struct inode *, off_t, int, struct buf **);
+static int ffs_balloc_ufs2(struct inode *, off_t, int, struct buf **);
+
+/*
+ * Balloc defines the structure of file system storage
+ * by allocating the physical blocks on a device given
+ * the inode and the logical block number in a file.
+ *
+ * Assume: flags == B_SYNC | B_CLRBUF
+ */
+
+int
+ffs_balloc(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+       if (ip->i_fs->fs_magic == FS_UFS2_MAGIC)
+               return ffs_balloc_ufs2(ip, offset, bufsize, bpp);
+       else
+               return ffs_balloc_ufs1(ip, offset, bufsize, bpp);
+}
+
+static int
+ffs_balloc_ufs1(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+       daddr_t lbn, lastlbn;
+       int size;
+       int32_t nb;
+       struct buf *bp, *nbp;
+       struct fs *fs = ip->i_fs;
+       struct indir indirs[UFS_NIADDR + 2];
+       daddr_t newb, pref;
+       int32_t *bap;
+       int osize, nsize, num, i, error;
+       int32_t *allocblk, allociblk[UFS_NIADDR + 1];
+       int32_t *allocib;
+       const int needswap = UFS_FSNEEDSWAP(fs);
+
+       lbn = ffs_lblkno(fs, offset);
+       size = ffs_blkoff(fs, offset) + bufsize;
+       if (bpp != NULL) {
+               *bpp = NULL;
+       }
+
+       assert(size <= fs->fs_bsize);
+       if (lbn < 0)
+               return (EFBIG);
+
+       /*
+        * If the next write will extend the file into a new block,
+        * and the file is currently composed of a fragment
+        * this fragment has to be extended to be a full block.
+        */
+
+       lastlbn = ffs_lblkno(fs, ip->i_ffs1_size);
+       if (lastlbn < UFS_NDADDR && lastlbn < lbn) {
+               nb = lastlbn;
+               osize = ffs_blksize(fs, ip, nb);
+               if (osize < fs->fs_bsize && osize > 0) {
+                       warnx("need to ffs_realloccg; not supported!");
+                       abort();
+               }
+       }
+
+       /*
+        * The first UFS_NDADDR blocks are direct blocks
+        */
+
+       if (lbn < UFS_NDADDR) {
+               nb = ufs_rw32(ip->i_ffs1_db[lbn], needswap);
+               if (nb != 0 && ip->i_ffs1_size >= ffs_lblktosize(fs, lbn + 1)) {
+
+                       /*
+                        * The block is an already-allocated direct block
+                        * and the file already extends past this block,
+                        * thus this must be a whole block.
+                        * Just read the block (if requested).
+                        */
+
+                       if (bpp != NULL) {
+                               error = bread(ip->i_devvp, lbn, fs->fs_bsize,
+                                   NULL, 0, bpp);
+                               if (error) {
+                                       brelse(*bpp, 0);
+                                       return (error);
+                               }
+                       }
+                       return (0);
+               }
+               if (nb != 0) {
+
+                       /*
+                        * Consider need to reallocate a fragment.
+                        */
+
+                       osize = ffs_fragroundup(fs, ffs_blkoff(fs, ip->i_ffs1_size));
+                       nsize = ffs_fragroundup(fs, size);
+                       if (nsize <= osize) {
+
+                               /*
+                                * The existing block is already
+                                * at least as big as we want.
+                                * Just read the block (if requested).
+                                */
+
+                               if (bpp != NULL) {
+                                       error = bread(ip->i_devvp, lbn, osize,
+                                           NULL, 0, bpp);
+                                       if (error) {
+                                               brelse(*bpp, 0);
+                                               return (error);
+                                       }
+                               }
+                               return 0;
+                       } else {
+                               warnx("need to ffs_realloccg; not supported!");
+                               abort();
+                       }
+               } else {
+
+                       /*
+                        * the block was not previously allocated,
+                        * allocate a new block or fragment.
+                        */
+
+                       if (ip->i_ffs1_size < ffs_lblktosize(fs, lbn + 1))
+                               nsize = ffs_fragroundup(fs, size);
+                       else
+                               nsize = fs->fs_bsize;
+                       error = ffs_alloc(ip, lbn,
+                           ffs_blkpref_ufs1(ip, lbn, (int)lbn,
+                               &ip->i_ffs1_db[0]),
+                               nsize, &newb);
+                       if (error)
+                               return (error);
+                       if (bpp != NULL) {
+                               bp = getblk(ip->i_devvp, lbn, nsize, 0, 0);
+                               bp->b_blkno = FFS_FSBTODB(fs, newb);
+                               clrbuf(bp);
+                               *bpp = bp;
+                       }
+               }
+               ip->i_ffs1_db[lbn] = ufs_rw32((int32_t)newb, needswap);
+               return (0);
+       }
+
+       /*
+        * Determine the number of levels of indirection.
+        */
+
+       pref = 0;
+       if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0)
+               return (error);
+
+       if (num < 1) {
+               warnx("ffs_balloc: ufs_getlbns returned indirect block");
+               abort();
+       }
+
+       /*
+        * Fetch the first indirect block allocating if necessary.
+        */
+
+       --num;
+       nb = ufs_rw32(ip->i_ffs1_ib[indirs[0].in_off], needswap);
+       allocib = NULL;
+       allocblk = allociblk;
+       if (nb == 0) {
+               pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0);
+               error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+               if (error)
+                       return error;
+               nb = newb;
+               *allocblk++ = nb;
+               bp = getblk(ip->i_devvp, indirs[1].in_lbn, fs->fs_bsize, 0, 0);
+               bp->b_blkno = FFS_FSBTODB(fs, nb);
+               clrbuf(bp);
+               /*
+                * Write synchronously so that indirect blocks
+                * never point at garbage.
+                */
+               if ((error = bwrite(bp)) != 0)
+                       return error;
+               allocib = &ip->i_ffs1_ib[indirs[0].in_off];
+               *allocib = ufs_rw32((int32_t)nb, needswap);
+       }
+
+       /*
+        * Fetch through the indirect blocks, allocating as necessary.
+        */
+
+       for (i = 1;;) {
+               error = bread(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize,
+                   NULL, 0, &bp);
+               if (error) {
+                       brelse(bp, 0);
+                       return error;
+               }
+               bap = (int32_t *)bp->b_data;
+               nb = ufs_rw32(bap[indirs[i].in_off], needswap);
+               if (i == num)
+                       break;
+               i++;
+               if (nb != 0) {
+                       brelse(bp, 0);
+                       continue;
+               }
+               if (pref == 0)
+                       pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0);
+               error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+               if (error) {
+                       brelse(bp, 0);
+                       return error;
+               }
+               nb = newb;
+               *allocblk++ = nb;
+               nbp = getblk(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize, 0, 0);
+               nbp->b_blkno = FFS_FSBTODB(fs, nb);
+               clrbuf(nbp);
+               /*
+                * Write synchronously so that indirect blocks
+                * never point at garbage.
+                */
+
+               if ((error = bwrite(nbp)) != 0) {
+                       brelse(bp, 0);
+                       return error;
+               }
+               bap[indirs[i - 1].in_off] = ufs_rw32(nb, needswap);
+
+               bwrite(bp);
+       }
+
+       /*
+        * Get the data block, allocating if necessary.
+        */
+
+       if (nb == 0) {
+               pref = ffs_blkpref_ufs1(ip, lbn, indirs[num].in_off, &bap[0]);
+               error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+               if (error) {
+                       brelse(bp, 0);
+                       return error;
+               }
+               nb = newb;
+               *allocblk++ = nb;
+               if (bpp != NULL) {
+                       nbp = getblk(ip->i_devvp, lbn, fs->fs_bsize, 0, 0);
+                       nbp->b_blkno = FFS_FSBTODB(fs, nb);
+                       clrbuf(nbp);
+                       *bpp = nbp;
+               }
+               bap[indirs[num].in_off] = ufs_rw32(nb, needswap);
+
+               /*
+                * If required, write synchronously, otherwise use
+                * delayed write.
+                */
+               bwrite(bp);
+               return (0);
+       }
+       brelse(bp, 0);
+       if (bpp != NULL) {
+               error = bread(ip->i_devvp, lbn, (int)fs->fs_bsize, NULL, 0,
+                   &nbp);
+               if (error) {
+                       brelse(nbp, 0);
+                       return error;
+               }
+               *bpp = nbp;
+       }
+       return (0);
+}
+
+static int
+ffs_balloc_ufs2(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+       daddr_t lbn, lastlbn;
+       int size;
+       struct buf *bp, *nbp;
+       struct fs *fs = ip->i_fs;
+       struct indir indirs[UFS_NIADDR + 2];
+       daddr_t newb, pref, nb;
+       int64_t *bap;
+       int osize, nsize, num, i, error;
+       int64_t *allocblk, allociblk[UFS_NIADDR + 1];
+       int64_t *allocib;
+       const int needswap = UFS_FSNEEDSWAP(fs);
+
+       lbn = ffs_lblkno(fs, offset);
+       size = ffs_blkoff(fs, offset) + bufsize;
+       if (bpp != NULL) {
+               *bpp = NULL;
+       }
+
+       assert(size <= fs->fs_bsize);
+       if (lbn < 0)
+               return (EFBIG);
+
+       /*
+        * If the next write will extend the file into a new block,
+        * and the file is currently composed of a fragment
+        * this fragment has to be extended to be a full block.
+        */
+
+       lastlbn = ffs_lblkno(fs, ip->i_ffs2_size);
+       if (lastlbn < UFS_NDADDR && lastlbn < lbn) {
+               nb = lastlbn;
+               osize = ffs_blksize(fs, ip, nb);
+               if (osize < fs->fs_bsize && osize > 0) {
+                       warnx("need to ffs_realloccg; not supported!");
+                       abort();
+               }
+       }
+
+       /*
+        * The first UFS_NDADDR blocks are direct blocks
+        */
+
+       if (lbn < UFS_NDADDR) {
+               nb = ufs_rw64(ip->i_ffs2_db[lbn], needswap);
+               if (nb != 0 && ip->i_ffs2_size >= ffs_lblktosize(fs, lbn + 1)) {
+
+                       /*
+                        * The block is an already-allocated direct block
+                        * and the file already extends past this block,
+                        * thus this must be a whole block.
+                        * Just read the block (if requested).
+                        */
+
+                       if (bpp != NULL) {
+                               error = bread(ip->i_devvp, lbn, fs->fs_bsize,
+                                   NULL, 0, bpp);
+                               if (error) {
+                                       brelse(*bpp, 0);
+                                       return (error);
+                               }
+                       }
+                       return (0);
+               }
+               if (nb != 0) {
+
+                       /*
+                        * Consider need to reallocate a fragment.
+                        */
+
+                       osize = ffs_fragroundup(fs, ffs_blkoff(fs, ip->i_ffs2_size));
+                       nsize = ffs_fragroundup(fs, size);
+                       if (nsize <= osize) {
+
+                               /*
+                                * The existing block is already
+                                * at least as big as we want.
+                                * Just read the block (if requested).
+                                */
+
+                               if (bpp != NULL) {
+                                       error = bread(ip->i_devvp, lbn, osize,
+                                           NULL, 0, bpp);
+                                       if (error) {
+                                               brelse(*bpp, 0);
+                                               return (error);
+                                       }
+                               }
+                               return 0;
+                       } else {
+                               warnx("need to ffs_realloccg; not supported!");
+                               abort();
+                       }
+               } else {
+
+                       /*
+                        * the block was not previously allocated,
+                        * allocate a new block or fragment.
+                        */
+
+                       if (ip->i_ffs2_size < ffs_lblktosize(fs, lbn + 1))
+                               nsize = ffs_fragroundup(fs, size);
+                       else
+                               nsize = fs->fs_bsize;
+                       error = ffs_alloc(ip, lbn,
+                           ffs_blkpref_ufs2(ip, lbn, (int)lbn,
+                               &ip->i_ffs2_db[0]),
+                               nsize, &newb);
+                       if (error)
+                               return (error);
+                       if (bpp != NULL) {
+                               bp = getblk(ip->i_devvp, lbn, nsize, 0, 0);
+                               bp->b_blkno = FFS_FSBTODB(fs, newb);
+                               clrbuf(bp);
+                               *bpp = bp;
+                       }
+               }
+               ip->i_ffs2_db[lbn] = ufs_rw64(newb, needswap);
+               return (0);
+       }
+
+       /*
+        * Determine the number of levels of indirection.
+        */
+
+       pref = 0;
+       if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0)
+               return (error);
+
+       if (num < 1) {
+               warnx("ffs_balloc: ufs_getlbns returned indirect block");
+               abort();
+       }
+
+       /*
+        * Fetch the first indirect block allocating if necessary.
+        */
+
+       --num;
+       nb = ufs_rw64(ip->i_ffs2_ib[indirs[0].in_off], needswap);
+       allocib = NULL;
+       allocblk = allociblk;
+       if (nb == 0) {
+               pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0);
+               error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+               if (error)
+                       return error;
+               nb = newb;
+               *allocblk++ = nb;
+               bp = getblk(ip->i_devvp, indirs[1].in_lbn, fs->fs_bsize, 0, 0);
+               bp->b_blkno = FFS_FSBTODB(fs, nb);
+               clrbuf(bp);
+               /*
+                * Write synchronously so that indirect blocks
+                * never point at garbage.
+                */
+               if ((error = bwrite(bp)) != 0)
+                       return error;
+               allocib = &ip->i_ffs2_ib[indirs[0].in_off];
+               *allocib = ufs_rw64(nb, needswap);
+       }
+
+       /*
+        * Fetch through the indirect blocks, allocating as necessary.
+        */
+
+       for (i = 1;;) {
+               error = bread(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize,
+                   NULL, 0, &bp);
+               if (error) {
+                       brelse(bp, 0);
+                       return error;
+               }
+               bap = (int64_t *)bp->b_data;
+               nb = ufs_rw64(bap[indirs[i].in_off], needswap);
+               if (i == num)
+                       break;
+               i++;
+               if (nb != 0) {
+                       brelse(bp, 0);
+                       continue;
+               }
+               if (pref == 0)
+                       pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0);
+               error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+               if (error) {
+                       brelse(bp, 0);
+                       return error;
+               }
+               nb = newb;
+               *allocblk++ = nb;
+               nbp = getblk(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize, 0, 0);
+               nbp->b_blkno = FFS_FSBTODB(fs, nb);
+               clrbuf(nbp);
+               /*
+                * Write synchronously so that indirect blocks
+                * never point at garbage.
+                */
+
+               if ((error = bwrite(nbp)) != 0) {
+                       brelse(bp, 0);
+                       return error;
+               }
+               bap[indirs[i - 1].in_off] = ufs_rw64(nb, needswap);
+
+               bwrite(bp);
+       }
+
+       /*
+        * Get the data block, allocating if necessary.
+        */
+
+       if (nb == 0) {
+               pref = ffs_blkpref_ufs2(ip, lbn, indirs[num].in_off, &bap[0]);
+               error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+               if (error) {
+                       brelse(bp, 0);
+                       return error;
+               }
+               nb = newb;
+               *allocblk++ = nb;
+               if (bpp != NULL) {
+                       nbp = getblk(ip->i_devvp, lbn, fs->fs_bsize, 0, 0);
+                       nbp->b_blkno = FFS_FSBTODB(fs, nb);
+                       clrbuf(nbp);
+                       *bpp = nbp;
+               }
+               bap[indirs[num].in_off] = ufs_rw64(nb, needswap);
+
+               /*
+                * If required, write synchronously, otherwise use
+                * delayed write.
+                */
+               bwrite(bp);
+               return (0);
+       }
+       brelse(bp, 0);
+       if (bpp != NULL) {
+               error = bread(ip->i_devvp, lbn, (int)fs->fs_bsize, NULL, 0,
+                   &nbp);
+               if (error) {
+                       brelse(nbp, 0);
+                       return error;
+               }
+               *bpp = nbp;
+       }
+       return (0);
+}
diff --git a/usr.sbin/makefs/ffs/ffs_extern.h b/usr.sbin/makefs/ffs/ffs_extern.h
new file mode 100644 (file)
index 0000000..53e9fe2
--- /dev/null
@@ -0,0 +1,76 @@
+/*     $NetBSD: ffs_extern.h,v 1.6 2003/08/07 11:25:33 agc Exp $       */
+/* From: NetBSD: ffs_extern.h,v 1.19 2001/08/17 02:18:48 lukem Exp */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *     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.
+ *
+ *     @(#)ffs_extern.h        8.6 (Berkeley) 3/30/95
+ */
+
+#include "ffs/buf.h"
+
+/*
+ * Structure used to pass around logical block paths generated by
+ * ufs_getlbns and used by truncate and bmap code.
+ */
+struct indir {
+       daddr_t in_lbn;         /* Logical block number. */
+       int     in_off;                 /* Offset in buffer. */
+       int     in_exists;              /* Flag if the block exists. */
+};
+
+       /* ffs.c */
+void panic(const char *, ...)
+    __attribute__((__noreturn__,__format__(__printf__,1,2)));  
+
+       /* ffs_alloc.c */
+int ffs_alloc(struct inode *, daddr_t, daddr_t, int, daddr_t *);
+daddr_t ffs_blkpref_ufs1(struct inode *, daddr_t, int, int32_t *);
+daddr_t ffs_blkpref_ufs2(struct inode *, daddr_t, int, int64_t *);
+void ffs_blkfree(struct inode *, daddr_t, long);
+void ffs_clusteracct(struct fs *, struct cg *, int32_t, int);
+
+       /* ffs_balloc.c */
+int ffs_balloc(struct inode *, off_t, int, struct buf **);
+
+       /* ffs_bswap.c */
+void ffs_sb_swap(struct fs*, struct fs *);
+void ffs_dinode1_swap(struct ufs1_dinode *, struct ufs1_dinode *);
+void ffs_dinode2_swap(struct ufs2_dinode *, struct ufs2_dinode *);
+void ffs_csum_swap(struct csum *, struct csum *, int);
+void ffs_cg_swap(struct cg *, struct cg *, struct fs *);
+
+       /* ffs_subr.c */
+void ffs_fragacct(struct fs *, int, int32_t[], int, int);
+int ffs_isblock(struct fs *, u_char *, int32_t);
+int ffs_isfreeblock(struct fs *, u_char *, int32_t);
+void ffs_clrblock(struct fs *, u_char *, int32_t);
+void ffs_setblock(struct fs *, u_char *, int32_t);
+
+       /* ufs_bmap.c */
+int ufs_getlbns(struct inode *, daddr_t, struct indir *, int *);
diff --git a/usr.sbin/makefs/ffs/mkfs.c b/usr.sbin/makefs/ffs/mkfs.c
new file mode 100644 (file)
index 0000000..c4dbc9b
--- /dev/null
@@ -0,0 +1,845 @@
+/*     $NetBSD: mkfs.c,v 1.32 2013/10/19 17:16:37 christos Exp $       */
+
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program
+ *
+ * Copyright (c) 1980, 1989, 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkfs.c     8.11 (Berkeley) 5/3/95";
+#else
+#ifdef __RCSID
+__RCSID("$NetBSD: mkfs.c,v 1.32 2013/10/19 17:16:37 christos Exp $");
+#endif
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "ffs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+#include "ffs/newfs_extern.h"
+
+static void initcg(int, time_t, const fsinfo_t *);
+static int ilog2(int);
+
+static int count_digits(int);
+
+/*
+ * make file system for cylinder-group style file systems
+ */
+#define        UMASK           0755
+#define        POWEROF2(num)   (((num) & ((num) - 1)) == 0)
+
+union {
+       struct fs fs;
+       char pad[SBLOCKSIZE];
+} fsun;
+#define        sblock  fsun.fs
+struct csum *fscs;
+
+union {
+       struct cg cg;
+       char pad[FFS_MAXBSIZE];
+} cgun;
+#define        acg     cgun.cg
+
+char *iobuf;
+int iobufsize;
+
+char writebuf[FFS_MAXBSIZE];
+
+static int     Oflag;     /* format as an 4.3BSD file system */
+static int64_t fssize;    /* file system size */
+static int     sectorsize;        /* bytes/sector */
+static int     fsize;     /* fragment size */
+static int     bsize;     /* block size */
+static int     maxbsize;   /* maximum clustering */
+static int     maxblkspercg;
+static int     minfree;           /* free space threshold */
+static int     opt;               /* optimization preference (space or time) */
+static int     density;           /* number of bytes per inode */
+static int     maxcontig;         /* max contiguous blocks to allocate */
+static int     maxbpg;    /* maximum blocks per file in a cyl group */
+static int     bbsize;    /* boot block size */
+static int     sbsize;    /* superblock size */
+static int     avgfilesize;       /* expected average file size */
+static int     avgfpdir;          /* expected number of files per directory */
+
+struct fs *
+ffs_mkfs(const char *fsys, const fsinfo_t *fsopts)
+{
+       int fragsperinode, optimalfpg, origdensity, minfpg, lastminfpg;
+       int32_t cylno, i, csfrags;
+       long long sizepb;
+       void *space;
+       int size;
+       int nprintcols, printcolwidth;
+       ffs_opt_t       *ffs_opts = fsopts->fs_specific;
+
+       Oflag =         ffs_opts->version;
+       fssize =        fsopts->size / fsopts->sectorsize;
+       sectorsize =    fsopts->sectorsize;
+       fsize =         ffs_opts->fsize;
+       bsize =         ffs_opts->bsize;
+       maxbsize =      ffs_opts->maxbsize;
+       maxblkspercg =  ffs_opts->maxblkspercg;
+       minfree =       ffs_opts->minfree;
+       opt =           ffs_opts->optimization;
+       density =       ffs_opts->density;
+       maxcontig =     ffs_opts->maxcontig;
+       maxbpg =        ffs_opts->maxbpg;
+       avgfilesize =   ffs_opts->avgfilesize;
+       avgfpdir =      ffs_opts->avgfpdir;
+       bbsize =        BBSIZE;
+       sbsize =        SBLOCKSIZE;
+
+       strlcpy((char *)sblock.fs_volname, ffs_opts->label,
+           sizeof(sblock.fs_volname));
+
+       if (Oflag == 0) {
+               sblock.fs_old_inodefmt = FS_42INODEFMT;
+               sblock.fs_maxsymlinklen = 0;
+               sblock.fs_old_flags = 0;
+       } else {
+               sblock.fs_old_inodefmt = FS_44INODEFMT;
+               sblock.fs_maxsymlinklen = (Oflag == 1 ? UFS1_MAXSYMLINKLEN :
+                   UFS2_MAXSYMLINKLEN);
+               sblock.fs_old_flags = FS_FLAGS_UPDATED;
+               sblock.fs_flags = 0;
+       }
+       /*
+        * Validate the given file system size.
+        * Verify that its last block can actually be accessed.
+        * Convert to file system fragment sized units.
+        */
+       if (fssize <= 0) {
+               printf("preposterous size %lld\n", (long long)fssize);
+               exit(13);
+       }
+       ffs_wtfs(fssize - 1, sectorsize, (char *)&sblock, fsopts);
+
+       /*
+        * collect and verify the filesystem density info
+        */
+       sblock.fs_avgfilesize = avgfilesize;
+       sblock.fs_avgfpdir = avgfpdir;
+       if (sblock.fs_avgfilesize <= 0)
+               printf("illegal expected average file size %d\n",
+                   sblock.fs_avgfilesize), exit(14);
+       if (sblock.fs_avgfpdir <= 0)
+               printf("illegal expected number of files per directory %d\n",
+                   sblock.fs_avgfpdir), exit(15);
+       /*
+        * collect and verify the block and fragment sizes
+        */
+       sblock.fs_bsize = bsize;
+       sblock.fs_fsize = fsize;
+       if (!POWEROF2(sblock.fs_bsize)) {
+               printf("block size must be a power of 2, not %d\n",
+                   sblock.fs_bsize);
+               exit(16);
+       }
+       if (!POWEROF2(sblock.fs_fsize)) {
+               printf("fragment size must be a power of 2, not %d\n",
+                   sblock.fs_fsize);
+               exit(17);
+       }
+       if (sblock.fs_fsize < sectorsize) {
+               printf("fragment size %d is too small, minimum is %d\n",
+                   sblock.fs_fsize, sectorsize);
+               exit(18);
+       }
+       if (sblock.fs_bsize < MINBSIZE) {
+               printf("block size %d is too small, minimum is %d\n",
+                   sblock.fs_bsize, MINBSIZE);
+               exit(19);
+       }
+       if (sblock.fs_bsize > FFS_MAXBSIZE) {
+               printf("block size %d is too large, maximum is %d\n",
+                   sblock.fs_bsize, FFS_MAXBSIZE);
+               exit(19);
+       }
+       if (sblock.fs_bsize < sblock.fs_fsize) {
+               printf("block size (%d) cannot be smaller than fragment size (%d)\n",
+                   sblock.fs_bsize, sblock.fs_fsize);
+               exit(20);
+       }
+
+       if (maxbsize < bsize || !POWEROF2(maxbsize)) {
+               sblock.fs_maxbsize = sblock.fs_bsize;
+               printf("Extent size set to %d\n", sblock.fs_maxbsize);
+       } else if (sblock.fs_maxbsize > FS_MAXCONTIG * sblock.fs_bsize) {
+               sblock.fs_maxbsize = FS_MAXCONTIG * sblock.fs_bsize;
+               printf("Extent size reduced to %d\n", sblock.fs_maxbsize);
+       } else {
+               sblock.fs_maxbsize = maxbsize;
+       }
+       sblock.fs_maxcontig = maxcontig;
+       if (sblock.fs_maxcontig < sblock.fs_maxbsize / sblock.fs_bsize) {
+               sblock.fs_maxcontig = sblock.fs_maxbsize / sblock.fs_bsize;
+               printf("Maxcontig raised to %d\n", sblock.fs_maxbsize);
+       }
+
+       if (sblock.fs_maxcontig > 1)
+               sblock.fs_contigsumsize = MIN(sblock.fs_maxcontig,FS_MAXCONTIG);
+
+       sblock.fs_bmask = ~(sblock.fs_bsize - 1);
+       sblock.fs_fmask = ~(sblock.fs_fsize - 1);
+       sblock.fs_qbmask = ~sblock.fs_bmask;
+       sblock.fs_qfmask = ~sblock.fs_fmask;
+       for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1)
+               sblock.fs_bshift++;
+       for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1)
+               sblock.fs_fshift++;
+       sblock.fs_frag = ffs_numfrags(&sblock, sblock.fs_bsize);
+       for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1)
+               sblock.fs_fragshift++;
+       if (sblock.fs_frag > MAXFRAG) {
+               printf("fragment size %d is too small, "
+                       "minimum with block size %d is %d\n",
+                   sblock.fs_fsize, sblock.fs_bsize,
+                   sblock.fs_bsize / MAXFRAG);
+               exit(21);
+       }
+       sblock.fs_fsbtodb = ilog2(sblock.fs_fsize / sectorsize);
+       sblock.fs_size = fssize = FFS_DBTOFSB(&sblock, fssize);
+
+       if (Oflag <= 1) {
+               sblock.fs_magic = FS_UFS1_MAGIC;
+               sblock.fs_sblockloc = SBLOCK_UFS1;
+               sblock.fs_nindir = sblock.fs_bsize / sizeof(int32_t);
+               sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs1_dinode);
+               sblock.fs_maxsymlinklen = ((UFS_NDADDR + UFS_NIADDR) *
+                   sizeof (int32_t));
+               sblock.fs_old_inodefmt = FS_44INODEFMT;
+               sblock.fs_old_cgoffset = 0;
+               sblock.fs_old_cgmask = 0xffffffff;
+               sblock.fs_old_size = sblock.fs_size;
+               sblock.fs_old_rotdelay = 0;
+               sblock.fs_old_rps = 60;
+               sblock.fs_old_nspf = sblock.fs_fsize / sectorsize;
+               sblock.fs_old_cpg = 1;
+               sblock.fs_old_interleave = 1;
+               sblock.fs_old_trackskew = 0;
+               sblock.fs_old_cpc = 0;
+               sblock.fs_old_postblformat = 1;
+               sblock.fs_old_nrpos = 1;
+       } else {
+               sblock.fs_magic = FS_UFS2_MAGIC;
+#if 0 /* XXX makefs is used for small filesystems. */
+               sblock.fs_sblockloc = SBLOCK_UFS2;
+#else
+               sblock.fs_sblockloc = SBLOCK_UFS1;
+#endif
+               sblock.fs_nindir = sblock.fs_bsize / sizeof(int64_t);
+               sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs2_dinode);
+               sblock.fs_maxsymlinklen = ((UFS_NDADDR + UFS_NIADDR) *
+                   sizeof (int64_t));
+       }
+
+       sblock.fs_sblkno =
+           roundup(howmany(sblock.fs_sblockloc + SBLOCKSIZE, sblock.fs_fsize),
+               sblock.fs_frag);
+       sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno +
+           roundup(howmany(SBLOCKSIZE, sblock.fs_fsize), sblock.fs_frag));
+       sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag;
+       sblock.fs_maxfilesize = sblock.fs_bsize * UFS_NDADDR - 1;
+       for (sizepb = sblock.fs_bsize, i = 0; i < UFS_NIADDR; i++) {
+               sizepb *= FFS_NINDIR(&sblock);
+               sblock.fs_maxfilesize += sizepb;
+       }
+
+       /*
+        * Calculate the number of blocks to put into each cylinder group.
+        *
+        * This algorithm selects the number of blocks per cylinder
+        * group. The first goal is to have at least enough data blocks
+        * in each cylinder group to meet the density requirement. Once
+        * this goal is achieved we try to expand to have at least
+        * 1 cylinder group. Once this goal is achieved, we pack as
+        * many blocks into each cylinder group map as will fit.
+        *
+        * We start by calculating the smallest number of blocks that we
+        * can put into each cylinder group. If this is too big, we reduce
+        * the density until it fits.
+        */
+       origdensity = density;
+       for (;;) {
+               fragsperinode = MAX(ffs_numfrags(&sblock, density), 1);
+               minfpg = fragsperinode * FFS_INOPB(&sblock);
+               if (minfpg > sblock.fs_size)
+                       minfpg = sblock.fs_size;
+               sblock.fs_ipg = FFS_INOPB(&sblock);
+               sblock.fs_fpg = roundup(sblock.fs_iblkno +
+                   sblock.fs_ipg / FFS_INOPF(&sblock), sblock.fs_frag);
+               if (sblock.fs_fpg < minfpg)
+                       sblock.fs_fpg = minfpg;
+               sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+                   FFS_INOPB(&sblock));
+               sblock.fs_fpg = roundup(sblock.fs_iblkno +
+                   sblock.fs_ipg / FFS_INOPF(&sblock), sblock.fs_frag);
+               if (sblock.fs_fpg < minfpg)
+                       sblock.fs_fpg = minfpg;
+               sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+                   FFS_INOPB(&sblock));
+               if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize)
+                       break;
+               density -= sblock.fs_fsize;
+       }
+       if (density != origdensity)
+               printf("density reduced from %d to %d\n", origdensity, density);
+
+       if (maxblkspercg <= 0 || maxblkspercg >= fssize)
+               maxblkspercg = fssize - 1;
+       /*
+        * Start packing more blocks into the cylinder group until
+        * it cannot grow any larger, the number of cylinder groups
+        * drops below 1, or we reach the size requested.
+        */
+       for ( ; sblock.fs_fpg < maxblkspercg; sblock.fs_fpg += sblock.fs_frag) {
+               sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+                   FFS_INOPB(&sblock));
+               if (sblock.fs_size / sblock.fs_fpg < 1)
+                       break;
+               if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize)
+                       continue;
+               if (CGSIZE(&sblock) == (unsigned long)sblock.fs_bsize)
+                       break;
+               sblock.fs_fpg -= sblock.fs_frag;
+               sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+                   FFS_INOPB(&sblock));
+               break;
+       }
+       /*
+        * Check to be sure that the last cylinder group has enough blocks
+        * to be viable. If it is too small, reduce the number of blocks
+        * per cylinder group which will have the effect of moving more
+        * blocks into the last cylinder group.
+        */
+       optimalfpg = sblock.fs_fpg;
+       for (;;) {
+               sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg);
+               lastminfpg = roundup(sblock.fs_iblkno +
+                   sblock.fs_ipg / FFS_INOPF(&sblock), sblock.fs_frag);
+               if (sblock.fs_size < lastminfpg) {
+                       printf("Filesystem size %lld < minimum size of %d\n",
+                           (long long)sblock.fs_size, lastminfpg);
+                       exit(28);
+               }
+               if (sblock.fs_size % sblock.fs_fpg >= lastminfpg ||
+                   sblock.fs_size % sblock.fs_fpg == 0)
+                       break;
+               sblock.fs_fpg -= sblock.fs_frag;
+               sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+                   FFS_INOPB(&sblock));
+       }
+       if (optimalfpg != sblock.fs_fpg)
+               printf("Reduced frags per cylinder group from %d to %d %s\n",
+                  optimalfpg, sblock.fs_fpg, "to enlarge last cyl group");
+       sblock.fs_cgsize = ffs_fragroundup(&sblock, CGSIZE(&sblock));
+       sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / FFS_INOPF(&sblock);
+       if (Oflag <= 1) {
+               sblock.fs_old_spc = sblock.fs_fpg * sblock.fs_old_nspf;
+               sblock.fs_old_nsect = sblock.fs_old_spc;
+               sblock.fs_old_npsect = sblock.fs_old_spc;
+               sblock.fs_old_ncyl = sblock.fs_ncg;
+       }
+
+       /*
+        * fill in remaining fields of the super block
+        */
+       sblock.fs_csaddr = cgdmin(&sblock, 0);
+       sblock.fs_cssize =
+           ffs_fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum));
+
+       /*
+        * Setup memory for temporary in-core cylgroup summaries.
+        * Cribbed from ffs_mountfs().
+        */
+       size = sblock.fs_cssize;
+       if (sblock.fs_contigsumsize > 0)
+               size += sblock.fs_ncg * sizeof(int32_t);
+       space = ecalloc(1, size);
+       sblock.fs_csp = space;
+       space = (char *)space + sblock.fs_cssize;
+       if (sblock.fs_contigsumsize > 0) {
+               int32_t *lp;
+
+               sblock.fs_maxcluster = lp = space;
+               for (i = 0; i < sblock.fs_ncg; i++)
+               *lp++ = sblock.fs_contigsumsize;
+       }
+
+       sblock.fs_sbsize = ffs_fragroundup(&sblock, sizeof(struct fs));
+       if (sblock.fs_sbsize > SBLOCKSIZE)
+               sblock.fs_sbsize = SBLOCKSIZE;
+       sblock.fs_minfree = minfree;
+       sblock.fs_maxcontig = maxcontig;
+       sblock.fs_maxbpg = maxbpg;
+       sblock.fs_optim = opt;
+       sblock.fs_cgrotor = 0;
+       sblock.fs_pendingblocks = 0;
+       sblock.fs_pendinginodes = 0;
+       sblock.fs_cstotal.cs_ndir = 0;
+       sblock.fs_cstotal.cs_nbfree = 0;
+       sblock.fs_cstotal.cs_nifree = 0;
+       sblock.fs_cstotal.cs_nffree = 0;
+       sblock.fs_fmod = 0;
+       sblock.fs_ronly = 0;
+       sblock.fs_state = 0;
+       sblock.fs_clean = FS_ISCLEAN;
+       sblock.fs_ronly = 0;
+       sblock.fs_id[0] = start_time.tv_sec;
+       sblock.fs_id[1] = random();
+       sblock.fs_fsmnt[0] = '\0';
+       csfrags = howmany(sblock.fs_cssize, sblock.fs_fsize);
+       sblock.fs_dsize = sblock.fs_size - sblock.fs_sblkno -
+           sblock.fs_ncg * (sblock.fs_dblkno - sblock.fs_sblkno);
+       sblock.fs_cstotal.cs_nbfree =
+           ffs_fragstoblks(&sblock, sblock.fs_dsize) -
+           howmany(csfrags, sblock.fs_frag);
+       sblock.fs_cstotal.cs_nffree =
+           ffs_fragnum(&sblock, sblock.fs_size) +
+           (ffs_fragnum(&sblock, csfrags) > 0 ?
+           sblock.fs_frag - ffs_fragnum(&sblock, csfrags) : 0);
+       sblock.fs_cstotal.cs_nifree = sblock.fs_ncg * sblock.fs_ipg - UFS_ROOTINO;
+       sblock.fs_cstotal.cs_ndir = 0;
+       sblock.fs_dsize -= csfrags;
+       sblock.fs_time = start_time.tv_sec;
+       if (Oflag <= 1) {
+               sblock.fs_old_time = start_time.tv_sec;
+               sblock.fs_old_dsize = sblock.fs_dsize;
+               sblock.fs_old_csaddr = sblock.fs_csaddr;
+               sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir;
+               sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree;
+               sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree;
+               sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree;
+       }
+       /*
+        * Dump out summary information about file system.
+        */
+#define        B2MBFACTOR (1 / (1024.0 * 1024.0))
+       printf("%s: %.1fMB (%lld sectors) block size %d, "
+              "fragment size %d\n",
+           fsys, (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
+           (long long)FFS_FSBTODB(&sblock, sblock.fs_size),
+           sblock.fs_bsize, sblock.fs_fsize);
+       printf("\tusing %d cylinder groups of %.2fMB, %d blks, "
+              "%d inodes.\n",
+           sblock.fs_ncg,
+           (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
+           sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg);
+#undef B2MBFACTOR
+       /*
+        * Now determine how wide each column will be, and calculate how
+        * many columns will fit in a 76 char line. 76 is the width of the
+        * subwindows in sysinst.
+        */
+       printcolwidth = count_digits(
+                       FFS_FSBTODB(&sblock, cgsblock(&sblock, sblock.fs_ncg -1)));
+       nprintcols = 76 / (printcolwidth + 2);
+
+       /*
+        * allocate space for superblock, cylinder group map, and
+        * two sets of inode blocks.
+        */
+       if (sblock.fs_bsize < SBLOCKSIZE)
+               iobufsize = SBLOCKSIZE + 3 * sblock.fs_bsize;
+       else
+               iobufsize = 4 * sblock.fs_bsize;
+       iobuf = ecalloc(1, iobufsize);
+       /*
+        * Make a copy of the superblock into the buffer that we will be
+        * writing out in each cylinder group.
+        */
+       memcpy(writebuf, &sblock, sbsize);
+       if (fsopts->needswap)
+               ffs_sb_swap(&sblock, (struct fs*)writebuf);
+       memcpy(iobuf, writebuf, SBLOCKSIZE);
+
+       printf("super-block backups (for fsck -b #) at:");
+       for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+               initcg(cylno, start_time.tv_sec, fsopts);
+               if (cylno % nprintcols == 0)
+                       printf("\n");
+               printf(" %*lld,", printcolwidth,
+                       (long long)FFS_FSBTODB(&sblock, cgsblock(&sblock, cylno)));
+               fflush(stdout);
+       }
+       printf("\n");
+
+       /*
+        * Now construct the initial file system,
+        * then write out the super-block.
+        */
+       sblock.fs_time = start_time.tv_sec;
+       if (Oflag <= 1) {
+               sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir;
+               sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree;
+               sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree;
+               sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree;
+       }
+       if (fsopts->needswap)
+               sblock.fs_flags |= FS_SWAPPED;
+       ffs_write_superblock(&sblock, fsopts);
+       return (&sblock);
+}
+
+/*
+ * Write out the superblock and its duplicates,
+ * and the cylinder group summaries
+ */
+void
+ffs_write_superblock(struct fs *fs, const fsinfo_t *fsopts)
+{
+       int cylno, size, blks, i, saveflag;
+       void *space;
+       char *wrbuf;
+
+       saveflag = fs->fs_flags & FS_INTERNAL;
+       fs->fs_flags &= ~FS_INTERNAL;
+
+        memcpy(writebuf, &sblock, sbsize);
+       if (fsopts->needswap)
+               ffs_sb_swap(fs, (struct fs*)writebuf);
+       ffs_wtfs(fs->fs_sblockloc / sectorsize, sbsize, writebuf, fsopts);
+
+       /* Write out the duplicate super blocks */
+       for (cylno = 0; cylno < fs->fs_ncg; cylno++)
+               ffs_wtfs(FFS_FSBTODB(fs, cgsblock(fs, cylno)),
+                   sbsize, writebuf, fsopts);
+
+       /* Write out the cylinder group summaries */
+       size = fs->fs_cssize;
+       blks = howmany(size, fs->fs_fsize);
+       space = (void *)fs->fs_csp;
+       wrbuf = emalloc(size);
+       for (i = 0; i < blks; i+= fs->fs_frag) {
+               size = fs->fs_bsize;
+               if (i + fs->fs_frag > blks)
+                       size = (blks - i) * fs->fs_fsize;
+               if (fsopts->needswap)
+                       ffs_csum_swap((struct csum *)space,
+                           (struct csum *)wrbuf, size);
+               else
+                       memcpy(wrbuf, space, (u_int)size);
+               ffs_wtfs(FFS_FSBTODB(fs, fs->fs_csaddr + i), size, wrbuf, fsopts);
+               space = (char *)space + size;
+       }
+       free(wrbuf);
+       fs->fs_flags |= saveflag;
+}
+
+/*
+ * Initialize a cylinder group.
+ */
+static void
+initcg(int cylno, time_t utime, const fsinfo_t *fsopts)
+{
+       daddr_t cbase, dmax;
+       int i, j, d, dlower, dupper, blkno;
+       struct ufs1_dinode *dp1;
+       struct ufs2_dinode *dp2;
+       int start;
+
+       /*
+        * Determine block bounds for cylinder group.
+        * Allow space for super block summary information in first
+        * cylinder group.
+        */
+       cbase = cgbase(&sblock, cylno);
+       dmax = cbase + sblock.fs_fpg;
+       if (dmax > sblock.fs_size)
+               dmax = sblock.fs_size;
+       dlower = cgsblock(&sblock, cylno) - cbase;
+       dupper = cgdmin(&sblock, cylno) - cbase;
+       if (cylno == 0)
+               dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
+       memset(&acg, 0, sblock.fs_cgsize);
+       acg.cg_time = utime;
+       acg.cg_magic = CG_MAGIC;
+       acg.cg_cgx = cylno;
+       acg.cg_niblk = sblock.fs_ipg;
+       acg.cg_initediblk = sblock.fs_ipg < 2 * FFS_INOPB(&sblock) ?
+           sblock.fs_ipg : 2 * FFS_INOPB(&sblock);
+       acg.cg_ndblk = dmax - cbase;
+       if (sblock.fs_contigsumsize > 0)
+               acg.cg_nclusterblks = acg.cg_ndblk >> sblock.fs_fragshift;
+       start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield);
+       if (Oflag == 2) {
+               acg.cg_iusedoff = start;
+       } else {
+               if (cylno == sblock.fs_ncg - 1)
+                       acg.cg_old_ncyl = howmany(acg.cg_ndblk,
+                           sblock.fs_fpg / sblock.fs_old_cpg);
+               else
+                       acg.cg_old_ncyl = sblock.fs_old_cpg;
+               acg.cg_old_time = acg.cg_time;
+               acg.cg_time = 0;
+               acg.cg_old_niblk = acg.cg_niblk;
+               acg.cg_niblk = 0;
+               acg.cg_initediblk = 0;
+               acg.cg_old_btotoff = start;
+               acg.cg_old_boff = acg.cg_old_btotoff +
+                   sblock.fs_old_cpg * sizeof(int32_t);
+               acg.cg_iusedoff = acg.cg_old_boff +
+                   sblock.fs_old_cpg * sizeof(u_int16_t);
+       }
+       acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT);
+       if (sblock.fs_contigsumsize <= 0) {
+               acg.cg_nextfreeoff = acg.cg_freeoff +
+                  howmany(sblock.fs_fpg, CHAR_BIT);
+       } else {
+               acg.cg_clustersumoff = acg.cg_freeoff +
+                   howmany(sblock.fs_fpg, CHAR_BIT) - sizeof(int32_t);
+               acg.cg_clustersumoff =
+                   roundup(acg.cg_clustersumoff, sizeof(int32_t));
+               acg.cg_clusteroff = acg.cg_clustersumoff +
+                   (sblock.fs_contigsumsize + 1) * sizeof(int32_t);
+               acg.cg_nextfreeoff = acg.cg_clusteroff +
+                   howmany(ffs_fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
+       }
+       if (acg.cg_nextfreeoff > sblock.fs_cgsize) {
+               printf("Panic: cylinder group too big\n");
+               exit(37);
+       }
+       acg.cg_cs.cs_nifree += sblock.fs_ipg;
+       if (cylno == 0) {
+               size_t r;
+
+               for (r = 0; r < UFS_ROOTINO; r++) {
+                       setbit(cg_inosused(&acg, 0), r);
+                       acg.cg_cs.cs_nifree--;
+               }
+       }
+       if (cylno > 0) {
+               /*
+                * In cylno 0, beginning space is reserved
+                * for boot and super blocks.
+                */
+               for (d = 0, blkno = 0; d < dlower;) {
+                       ffs_setblock(&sblock, cg_blksfree(&acg, 0), blkno);
+                       if (sblock.fs_contigsumsize > 0)
+                               setbit(cg_clustersfree(&acg, 0), blkno);
+                       acg.cg_cs.cs_nbfree++;
+                       d += sblock.fs_frag;
+                       blkno++;
+               }
+       }
+       if ((i = (dupper & (sblock.fs_frag - 1))) != 0) {
+               acg.cg_frsum[sblock.fs_frag - i]++;
+               for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
+                       setbit(cg_blksfree(&acg, 0), dupper);
+                       acg.cg_cs.cs_nffree++;
+               }
+       }
+       for (d = dupper, blkno = dupper >> sblock.fs_fragshift;
+            d + sblock.fs_frag <= acg.cg_ndblk; ) {
+               ffs_setblock(&sblock, cg_blksfree(&acg, 0), blkno);
+               if (sblock.fs_contigsumsize > 0)
+                       setbit(cg_clustersfree(&acg, 0), blkno);
+               acg.cg_cs.cs_nbfree++;
+               d += sblock.fs_frag;
+               blkno++;
+       }
+       if (d < acg.cg_ndblk) {
+               acg.cg_frsum[acg.cg_ndblk - d]++;
+               for (; d < acg.cg_ndblk; d++) {
+                       setbit(cg_blksfree(&acg, 0), d);
+                       acg.cg_cs.cs_nffree++;
+               }
+       }
+       if (sblock.fs_contigsumsize > 0) {
+               int32_t *sump = cg_clustersum(&acg, 0);
+               u_char *mapp = cg_clustersfree(&acg, 0);
+               int map = *mapp++;
+               int bit = 1;
+               int run = 0;
+
+               for (i = 0; i < acg.cg_nclusterblks; i++) {
+                       if ((map & bit) != 0) {
+                               run++;
+                       } else if (run != 0) {
+                               if (run > sblock.fs_contigsumsize)
+                                       run = sblock.fs_contigsumsize;
+                               sump[run]++;
+                               run = 0;
+                       }
+                       if ((i & (CHAR_BIT - 1)) != (CHAR_BIT - 1)) {
+                               bit <<= 1;
+                       } else {
+                               map = *mapp++;
+                               bit = 1;
+                       }
+               }
+               if (run != 0) {
+                       if (run > sblock.fs_contigsumsize)
+                               run = sblock.fs_contigsumsize;
+                       sump[run]++;
+               }
+       }
+       sblock.fs_cs(&sblock, cylno) = acg.cg_cs;
+       /*
+        * Write out the duplicate super block, the cylinder group map
+        * and two blocks worth of inodes in a single write.
+        */
+       start = sblock.fs_bsize > SBLOCKSIZE ? sblock.fs_bsize : SBLOCKSIZE;
+       memcpy(&iobuf[start], &acg, sblock.fs_cgsize);
+       if (fsopts->needswap)
+               ffs_cg_swap(&acg, (struct cg*)&iobuf[start], &sblock);
+       start += sblock.fs_bsize;
+       dp1 = (struct ufs1_dinode *)(&iobuf[start]);
+       dp2 = (struct ufs2_dinode *)(&iobuf[start]);
+       for (i = 0; i < acg.cg_initediblk; i++) {
+               if (sblock.fs_magic == FS_UFS1_MAGIC) {
+                       /* No need to swap, it'll stay random */
+                       dp1->di_gen = random();
+                       dp1++;
+               } else {
+                       dp2->di_gen = random();
+                       dp2++;
+               }
+       }
+       ffs_wtfs(FFS_FSBTODB(&sblock, cgsblock(&sblock, cylno)), iobufsize, iobuf,
+           fsopts);
+       /*
+        * For the old file system, we have to initialize all the inodes.
+        */
+       if (Oflag <= 1) {
+               for (i = 2 * sblock.fs_frag;
+                    i < sblock.fs_ipg / FFS_INOPF(&sblock);
+                    i += sblock.fs_frag) {
+                       dp1 = (struct ufs1_dinode *)(&iobuf[start]);
+                       for (j = 0; j < FFS_INOPB(&sblock); j++) {
+                               dp1->di_gen = random();
+                               dp1++;
+                       }
+                       ffs_wtfs(FFS_FSBTODB(&sblock, cgimin(&sblock, cylno) + i),
+                           sblock.fs_bsize, &iobuf[start], fsopts);
+               }
+       }
+}
+
+/*
+ * read a block from the file system
+ */
+void
+ffs_rdfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts)
+{
+       int n;
+       off_t offset;
+
+       offset = bno * fsopts->sectorsize + fsopts->offset;
+       if (lseek(fsopts->fd, offset, SEEK_SET) < 0)
+               err(1, "%s: seek error for sector %lld", __func__,
+                   (long long)bno);
+       n = read(fsopts->fd, bf, size);
+       if (n == -1) {
+               err(1, "%s: read error bno %lld size %d", __func__,
+                   (long long)bno, size);
+       }
+       else if (n != size)
+               errx(1, "%s: short read error for sector %lld", __func__,
+                   (long long)bno);
+}
+
+/*
+ * write a block to the file system
+ */
+void
+ffs_wtfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts)
+{
+       int n;
+       off_t offset;
+
+       offset = bno * fsopts->sectorsize + fsopts->offset;
+       if (lseek(fsopts->fd, offset, SEEK_SET) == -1)
+               err(1, "%s: seek error for sector %lld", __func__,
+                   (long long)bno);
+       n = write(fsopts->fd, bf, size);
+       if (n == -1)
+               err(1, "%s: write error for sector %lld", __func__,
+                   (long long)bno);
+       else if (n != size)
+               errx(1, "%s: short write error for sector %lld", __func__,
+                   (long long)bno);
+}
+
+
+/* Determine how many digits are needed to print a given integer */
+static int
+count_digits(int num)
+{
+       int ndig;
+
+       for(ndig = 1; num > 9; num /=10, ndig++);
+
+       return (ndig);
+}
+
+static int
+ilog2(int val)
+{
+       u_int n;
+
+       for (n = 0; n < sizeof(n) * CHAR_BIT; n++)
+               if (1 << n == val)
+                       return (n);
+       errx(1, "%s: %d is not a power of 2", __func__, val);
+}
diff --git a/usr.sbin/makefs/ffs/newfs_extern.h b/usr.sbin/makefs/ffs/newfs_extern.h
new file mode 100644 (file)
index 0000000..d86b248
--- /dev/null
@@ -0,0 +1,34 @@
+/*     $NetBSD: newfs_extern.h,v 1.3 2009/10/21 01:07:47 snj Exp $     */
+/* From: NetBSD: extern.h,v 1.3 2000/12/01 12:03:27 simonb Exp $ */
+
+/*
+ * Copyright (c) 1997 Christos Zoulas.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/* prototypes */
+struct fs      *ffs_mkfs(const char *, const fsinfo_t *);
+void           ffs_write_superblock(struct fs *, const fsinfo_t *);
+void           ffs_rdfs(daddr_t, int, void *, const fsinfo_t *);
+void           ffs_wtfs(daddr_t, int, void *, const fsinfo_t *);
+
+#define        FFS_MAXBSIZE    65536
diff --git a/usr.sbin/makefs/ffs/ufs_bmap.c b/usr.sbin/makefs/ffs/ufs_bmap.c
new file mode 100644 (file)
index 0000000..1135b65
--- /dev/null
@@ -0,0 +1,145 @@
+/*     $NetBSD: ufs_bmap.c,v 1.18 2013/06/19 17:51:27 dholland Exp $   */
+/* From: NetBSD: ufs_bmap.c,v 1.14 2001/11/08 05:00:51 chs Exp */
+
+/*
+ * Copyright (c) 1989, 1991, 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.
+ *
+ *     @(#)ufs_bmap.c  8.8 (Berkeley) 8/11/95
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: ufs_bmap.c,v 1.18 2013/06/19 17:51:27 dholland Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <strings.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block.  The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ffs_ib and
+ * once with the offset into the page itself.
+ */
+int
+ufs_getlbns(struct inode *ip, daddr_t bn, struct indir *ap, int *nump)
+{
+       daddr_t metalbn, realbn;
+       int64_t blockcnt;
+       int lbc;
+       int i, numlevels, off;
+       u_long lognindir;
+
+       lognindir = ffs(FFS_NINDIR(ip->i_fs)) - 1;
+       if (nump)
+               *nump = 0;
+       numlevels = 0;
+       realbn = bn;
+       if ((long)bn < 0)
+               bn = -(long)bn;
+
+       assert (bn >= UFS_NDADDR);
+
+       /* 
+        * Determine the number of levels of indirection.  After this loop
+        * is done, blockcnt indicates the number of data blocks possible
+        * at the given level of indirection, and UFS_NIADDR - i is the number
+        * of levels of indirection needed to locate the requested block.
+        */
+
+       bn -= UFS_NDADDR;
+       for (lbc = 0, i = UFS_NIADDR;; i--, bn -= blockcnt) {
+               if (i == 0)
+                       return (EFBIG);
+
+               lbc += lognindir;
+               blockcnt = (int64_t)1 << lbc;
+
+               if (bn < blockcnt)
+                       break;
+       }
+
+       /* Calculate the address of the first meta-block. */
+       metalbn = -((realbn >= 0 ? realbn : -realbn) - bn + UFS_NIADDR - i);
+
+       /* 
+        * At each iteration, off is the offset into the bap array which is
+        * an array of disk addresses at the current level of indirection.
+        * The logical block number and the offset in that block are stored
+        * into the argument array.
+        */
+       ap->in_lbn = metalbn;
+       ap->in_off = off = UFS_NIADDR - i;
+       ap->in_exists = 0;
+       ap++;
+       for (++numlevels; i <= UFS_NIADDR; i++) {
+               /* If searching for a meta-data block, quit when found. */
+               if (metalbn == realbn)
+                       break;
+
+               lbc -= lognindir;
+               blockcnt = (int64_t)1 << lbc;
+               off = (bn >> lbc) & (FFS_NINDIR(ip->i_fs) - 1);
+
+               ++numlevels;
+               ap->in_lbn = metalbn;
+               ap->in_off = off;
+               ap->in_exists = 0;
+               ++ap;
+
+               metalbn -= -1 + (off << lbc);
+       }
+       if (nump)
+               *nump = numlevels;
+       return (0);
+}
diff --git a/usr.sbin/makefs/ffs/ufs_inode.h b/usr.sbin/makefs/ffs/ufs_inode.h
new file mode 100644 (file)
index 0000000..cc2d589
--- /dev/null
@@ -0,0 +1,112 @@
+/*     $NetBSD: ufs_inode.h,v 1.5 2013/01/30 19:19:19 christos Exp $   */
+/* From:  NetBSD: inode.h,v 1.27 2001/12/18 10:57:23 fvdl 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.
+ *
+ *     @(#)inode.h     8.9 (Berkeley) 5/14/95
+ */
+
+union dinode {
+       struct ufs1_dinode ffs1_din;
+       struct ufs2_dinode ffs2_din;
+};
+
+struct inode {
+       ino_t           i_number;       /* The identity of the inode. */
+       struct vnode    *i_devvp;       /* device vnode for block I/O */
+       struct fs       *i_fs;          /* File system */
+       union dinode    i_din;
+       uint64_t        i_size;
+};
+
+#define        i_ffs1_atime            i_din.ffs1_din.di_atime
+#define        i_ffs1_atimensec        i_din.ffs1_din.di_atimensec
+#define        i_ffs1_blocks           i_din.ffs1_din.di_blocks
+#define        i_ffs1_ctime            i_din.ffs1_din.di_ctime
+#define        i_ffs1_ctimensec        i_din.ffs1_din.di_ctimensec
+#define        i_ffs1_db               i_din.ffs1_din.di_db
+#define        i_ffs1_flags            i_din.ffs1_din.di_flags
+#define        i_ffs1_gen              i_din.ffs1_din.di_gen
+#define        i_ffs11_gid             i_din.ffs1_din.di_gid
+#define        i_ffs1_ib               i_din.ffs1_din.di_ib
+#define        i_ffs1_mode             i_din.ffs1_din.di_mode
+#define        i_ffs1_mtime            i_din.ffs1_din.di_mtime
+#define        i_ffs1_mtimensec        i_din.ffs1_din.di_mtimensec
+#define        i_ffs1_nlink            i_din.ffs1_din.di_nlink
+#define        i_ffs1_rdev             i_din.ffs1_din.di_rdev
+#define        i_ffs1_shortlink        i_din.ffs1_din.db
+#define        i_ffs1_size             i_din.ffs1_din.di_size
+#define        i_ffs1_uid              i_din.ffs1_din.di_uid
+
+#define        i_ffs2_atime            i_din.ffs2_din.di_atime
+#define        i_ffs2_atimensec        i_din.ffs2_din.di_atimensec
+#define        i_ffs2_blocks           i_din.ffs2_din.di_blocks
+#define        i_ffs2_ctime            i_din.ffs2_din.di_ctime
+#define        i_ffs2_ctimensec        i_din.ffs2_din.di_ctimensec
+#define        i_ffs2_birthtime        i_din.ffs2_din.di_birthtime
+#define        i_ffs2_birthnsec        i_din.ffs2_din.di_birthnsec
+#define        i_ffs2_db               i_din.ffs2_din.di_db
+#define        i_ffs2_flags            i_din.ffs2_din.di_flags
+#define        i_ffs2_gen              i_din.ffs2_din.di_gen
+#define        i_ffs21_gid             i_din.ffs2_din.di_gid
+#define        i_ffs2_ib               i_din.ffs2_din.di_ib
+#define        i_ffs2_mode             i_din.ffs2_din.di_mode
+#define        i_ffs2_mtime            i_din.ffs2_din.di_mtime
+#define        i_ffs2_mtimensec        i_din.ffs2_din.di_mtimensec
+#define        i_ffs2_nlink            i_din.ffs2_din.di_nlink
+#define        i_ffs2_rdev             i_din.ffs2_din.di_rdev
+#define        i_ffs2_shortlink        i_din.ffs2_din.db
+#define        i_ffs2_size             i_din.ffs2_din.di_size
+#define        i_ffs2_uid              i_din.ffs2_din.di_uid
+
+#undef DIP
+#define DIP(ip, field) \
+        (((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) ? \
+        (ip)->i_ffs1_##field : (ip)->i_ffs2_##field)
+
+#define DIP_ASSIGN(ip, field, value)                                   \
+       do {                                                            \
+               if ((ip)->i_fs->fs_magic == FS_UFS1_MAGIC)              \
+                       (ip)->i_ffs1_##field = (value);                 \
+               else                                                    \
+                       (ip)->i_ffs2_##field = (value);                 \
+       } while(0)
+
+#define DIP_ADD(ip, field, value)                                      \
+       do {                                                            \
+               if ((ip)->i_fs->fs_magic == FS_UFS1_MAGIC)              \
+                       (ip)->i_ffs1_##field += (value);                \
+               else                                                    \
+                       (ip)->i_ffs2_##field += (value);                \
+       } while(0)
diff --git a/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8
new file mode 100644 (file)
index 0000000..92f8fb4
--- /dev/null
@@ -0,0 +1,460 @@
+.\"    $NetBSD: makefs.8,v 1.53 2013/08/06 20:16:54 wiz Exp $
+.\"
+.\" Copyright (c) 2001-2003 Wasabi Systems, Inc.
+.\" All rights reserved.
+.\"
+.\" Written by Luke Mewburn for Wasabi Systems, 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. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"      This product includes software developed for the NetBSD Project by
+.\"      Wasabi Systems, Inc.
+.\" 4. The name of Wasabi Systems, Inc. may not be used to endorse
+.\"    or promote products derived from this software without specific prior
+.\"    written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+.\" 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.
+.\"
+.Dd August 6, 2013
+.Dt MAKEFS 8
+.Os
+.Sh NAME
+.Nm makefs
+.Nd create a file system image from a directory tree
+.Sh SYNOPSIS
+.Nm
+.Op Fl rxZ
+.Op Fl B Ar endian
+.Op Fl b Ar free-blocks
+.Op Fl d Ar debug-mask
+.Op Fl F Ar mtree-specfile
+.Op Fl f Ar free-files
+.Op Fl M Ar minimum-size
+.Op Fl m Ar maximum-size
+.Op Fl N Ar userdb-dir
+.Op Fl O Ar offset
+.Op Fl o Ar fs-options
+.Op Fl S Ar sector-size
+.Op Fl s Ar image-size
+.Op Fl t Ar fs-type
+.Ar image-file
+.Ar directory
+.Op Ar extra-directory ...
+.Sh DESCRIPTION
+The utility
+.Nm
+creates a file system image into
+.Ar image-file
+from the directory tree
+.Ar directory .
+If any optional directory trees are passed in the
+.Ar extra-directory
+arguments, then the directory tree of each argument will be merged
+into the
+.Ar directory
+first before creating
+.Ar image-file .
+No special devices or privileges are required to perform this task.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl B Ar endian
+Set the byte order of the image to
+.Ar endian .
+Valid byte orders are
+.Ql 4321 ,
+.Ql big ,
+or
+.Ql be
+for big endian, and
+.Ql 1234 ,
+.Ql little ,
+or
+.Ql le
+for little endian.
+Some file systems may have a fixed byte order; in those cases this
+argument will be ignored.
+.It Fl b Ar free-blocks
+Ensure that a minimum of
+.Ar free-blocks
+free blocks exist in the image.
+An optional
+.Ql %
+suffix may be provided to indicate that
+.Ar free-blocks
+indicates a percentage of the calculated image size.
+.It Fl d Ar debug-mask
+Enable various levels of debugging, depending upon which bits are
+set in
+.Ar debug-mask .
+XXX: document these
+.It Fl F Ar mtree-specfile
+Use
+.Ar mtree-specfile
+as an
+.Xr mtree 8
+.Sq specfile
+specification.
+.Pp
+If a specfile entry exists in the underlying file system, its
+permissions and modification time will be used unless specifically
+overridden by the specfile.
+An error will be raised if the type of entry in the specfile
+conflicts with that of an existing entry.
+.Pp
+In the opposite case (where a specfile entry does not have an entry
+in the underlying file system) the following occurs:
+If the specfile entry is marked
+.Sy optional ,
+the specfile entry is ignored.
+Otherwise, the entry will be created in the image, and it is
+necessary to specify at least the following parameters in the
+specfile:
+.Sy type ,
+.Sy mode ,
+.Sy gname ,
+or
+.Sy gid ,
+and
+.Sy uname
+or
+.Sy uid ,
+.Sy device
+(in the case of block or character devices), and
+.Sy link
+(in the case of symbolic links).
+If
+.Sy time
+isn't provided, the current time will be used.
+If
+.Sy flags
+isn't provided, the current file flags will be used.
+Missing regular file entries will be created as zero-length files.
+.It Fl f Ar free-files
+Ensure that a minimum of
+.Ar free-files
+free files (inodes) exist in the image.
+An optional
+.Ql %
+suffix may be provided to indicate that
+.Ar free-files
+indicates a percentage of the calculated image size.
+.It Fl M Ar minimum-size
+Set the minimum size of the file system image to
+.Ar minimum-size .
+.It Fl m Ar maximum-size
+Set the maximum size of the file system image to
+.Ar maximum-size .
+An error will be raised if the target file system needs to be larger
+than this to accommodate the provided directory tree.
+.It Fl N Ar userdb-dir
+Use the user database text file
+.Pa master.passwd
+and group database text file
+.Pa group
+from
+.Ar userdb-dir ,
+rather than using the results from the system's
+.Xr getpwnam 3
+and
+.Xr getgrnam 3
+(and related) library calls.
+.It Fl O Ar offset
+Instead of creating the filesystem at the beginning of the file, start
+at offset.
+Valid only for
+.Sy ffs
+and
+.Sy msdos .
+.It Fl o Ar fs-options
+Set file system specific options.
+.Ar fs-options
+is a comma separated list of options.
+Valid file system specific options are detailed below.
+.It Fl r
+When merging multiple directories replace duplicate files with the last found.
+.It Fl S Ar sector-size
+Set the file system sector size to
+.Ar sector-size .
+.\" XXX: next line also true for cd9660?
+Defaults to 512.
+.It Fl s Ar image-size
+Set the size of the file system image to
+.Ar image-size .
+.It Fl t Ar fs-type
+Create an
+.Ar fs-type
+file system image.
+The following file system types are supported:
+.Bl -tag -width cd9660 -offset indent
+.It Sy ffs
+BSD fast file system (default).
+.It Sy cd9660
+ISO 9660 file system.
+.It Sy chfs
+Chip flash file system.
+.It Sy msdos
+FAT12, FAT16, or FAT32 file system.
+.It Sy v7fs
+7th Edition(V7) file system.
+.It Sy udf
+ISO/Ecma UDF file system.
+.El
+.It Fl x
+Exclude file system nodes not explicitly listed in the specfile.
+.It Fl Z
+Create a sparse file for
+.Sy ffs .
+This is useful for virtual machine images.
+.El
+.Pp
+Where sizes are specified, a decimal number of bytes is expected.
+Two or more numbers may be separated by an
+.Dq x
+to indicate a product.
+Each number may have one of the following optional suffixes:
+.Bl -tag -width 3n -offset indent -compact
+.It b
+Block; multiply by 512
+.It k
+Kibi; multiply by 1024 (1 KiB)
+.It m
+Mebi; multiply by 1048576 (1 MiB)
+.It g
+Gibi; multiply by 1073741824 (1 GiB)
+.It t
+Tebi; multiply by 1099511627776 (1 TiB)
+.It w
+Word; multiply by the number of bytes in an integer
+.El
+.\"
+.\"
+.Ss FFS-specific options
+.Sy ffs
+images have ffs-specific optional parameters that may be provided.
+Each of the options consists of a keyword, an equal sign
+.Pq Ql = ,
+and a value.
+The following keywords are supported:
+.Pp
+.Bl -tag -width optimization -offset indent -compact
+.It Sy avgfilesize
+Expected average file size.
+.It Sy avgfpdir
+Expected number of files per directory.
+.It Sy bsize
+Block size.
+.It Sy density
+Bytes per inode.
+.It Sy fsize
+Fragment size.
+.It Sy label
+Label name of the image.
+.It Sy maxbpg
+Maximum blocks per file in a cylinder group.
+.It Sy minfree
+Minimum % free.
+.It Sy optimization
+Optimization preference; one of
+.Ql space
+or
+.Ql time .
+.It Sy extent
+Maximum extent size.
+.It Sy maxbpcg
+Maximum total number of blocks in a cylinder group.
+.It Sy version
+UFS version.
+1 for FFS (default), 2 for UFS2.
+.El
+.Ss CD9660-specific options
+.Sy cd9660
+images have ISO9660-specific optional parameters that may be
+provided.
+The arguments consist of a keyword and, optionally, an equal sign
+.Pq Ql = ,
+and a value.
+The following keywords are supported:
+.Pp
+.Bl -tag -width omit-trailing-period -offset indent -compact
+.It Sy allow-deep-trees
+Allow the directory structure to exceed the maximum specified in
+the spec.
+.\" .It Sy allow-illegal-chars
+.\" Unknown
+.\" .It Sy allow-lowercase
+.\" Unknown
+.It Sy allow-max-name
+Allow 37 instead of 33 characters for filenames by omitting the
+version id.
+.It Sy allow-multidot
+Allow multiple dots in a filename.
+.It Sy applicationid
+Application ID of the image.
+.It Sy archimedes
+Use the
+.Ql ARCHIMEDES
+extension to encode
+.Tn RISC OS
+metadata.
+.It Sy chrp-boot
+Write an MBR partition table to the image to allow older CHRP hardware to
+boot.
+.It Sy boot-load-segment
+Set load segment for the boot image.
+.It Sy bootimage
+Filename of a boot image in the format
+.Dq sysid;filename ,
+where
+.Dq sysid
+is one of
+.Ql i386 ,
+.Ql mac68k ,
+.Ql macppc ,
+or
+.Ql powerpc .
+.It Sy generic-bootimage
+Load a generic boot image into the first 32K of the cd9660 image.
+.It Sy hard-disk-boot
+Boot image is a hard disk image.
+.It Sy keep-bad-images
+Don't throw away images whose write was aborted due to an error.
+For debugging purposes.
+.It Sy label
+Label name of the image.
+.It Sy no-boot
+Boot image is not bootable.
+.It Sy no-emul-boot
+Boot image is a
+.Dq no emulation
+ElTorito image.
+.It Sy no-trailing-padding
+Do not pad the image (apparently Linux needs the padding).
+.\" .It Sy omit-trailing-period
+.\" Unknown
+.It Sy preparer
+Preparer ID of the image.
+.It Sy publisher
+Publisher ID of the image.
+.It Sy rockridge
+Use RockRidge extensions (for longer filenames, etc.).
+.It Sy volumeid
+Volume set identifier of the image.
+.El
+.Ss CHFS-specific options
+.Sy chfs
+images have chfs-specific optional parameters that may be provided.
+Each of the options consists of a keyword, an equal sign
+.Pq Ql = ,
+and a value.
+The following keywords are supported:
+.Pp
+.Bl -tag -width optimization -offset indent -compact
+.It Sy pagesize
+Pagesize.
+.It Sy erasesize
+Erase block size of the media.
+.It Sy mediatype
+Type of the media.
+NOR: 0 or NAND: 1.
+.El
+.Ss msdos-specific options
+See
+.Xr newfs_msdos 8
+for fs specific options.
+.Ss V7FS-specific options
+The following keywords are supported:
+.Pp
+.Bl -tag -width optimization -offset indent -compact
+.It Sy pdp
+PDP endian.
+.It Sy progress
+Display a progress meter for the file system construction and file
+population.
+.El
+.Ss UDF-specific options
+.Sy udf
+images have udf-specific optional parameters that may be provided.
+Each of the options consists of a keyword, an equal sign
+.Pq Ql = ,
+and a value.
+The following keywords are supported:
+.Pp
+.Bl -tag -width optimization -compact
+.It Sy disctype
+This can have the following values:
+.Bl -tag -width cdromXdvdromXbdromXXX -compact
+.It Sy cdrom , Sy dvdrom , Sy bdrom
+create a read-only fs
+.It Sy dvdram , Sy bdre , Sy disk
+create a rewritable fs without sparing for defective sectors
+.It Sy cdr , Sy dvdr , Sy bdr
+create a rewritable fs on once recordable media using a VAT
+.It Sy cdrw , Sy dvdrw
+create a rewritable fs with sparing for defective sectors
+.El
+When an optical media is selected here, the sectorsize and the default disc
+size is assumed unless given explicitly.
+For rom images the disc size is the minimum needed.
+.It Sy loglabel
+Set the logical volume label of the disc to the specified argument.
+.It Sy discid
+Set the physical volume label of the disc to the specified argument.
+Prepend the physical volume label with a volumeset label separated
+with a ':' if wanted.
+For strict conformance and interchange, don't set the volumeset label
+manually unless it has an unique hex number in the first 8 character
+positions.
+.It Sy minver
+Set the minimum UDF version to be used.
+Choose UDF version numbers from 0x102, 0x150, 0x200, and 0x201.
+Versions 0x250 and 0x260 are currently not supported
+in
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr strsuftoll 3 ,
+.Xr installboot 8 ,
+.Xr mtree 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Nx 1.6 .
+.Sh AUTHORS
+.An Luke Mewburn
+.Aq lukem@NetBSD.org
+(original program),
+.An Daniel Watt ,
+.An Walter Deignan ,
+.An Ryan Gabrys ,
+.An Alan Perez-Rathke ,
+.An Ram Vedam
+(cd9660 support),
+.An UCHIYAMA Yasushi
+(v7fs support),
+.An Tamas Toth
+(chfs support).
+.An Christos Zoulas
+(msdos support).
+.An Reinoud Zandijk
+(udf support).
diff --git a/usr.sbin/makefs/makefs.c b/usr.sbin/makefs/makefs.c
new file mode 100644 (file)
index 0000000..116eceb
--- /dev/null
@@ -0,0 +1,437 @@
+/*     $NetBSD: makefs.c,v 1.50 2013/08/05 14:41:57 reinoud Exp $      */
+
+/*
+ * Copyright (c) 2001-2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: makefs.c,v 1.50 2013/08/05 14:41:57 reinoud Exp $");
+#endif /* !__lint */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "mtree.h"
+#include "cd9660.h"
+
+/*
+ * list of supported file systems and dispatch functions
+ */
+typedef struct {
+       const char      *type;
+       void            (*prepare_options)(fsinfo_t *);
+       int             (*parse_options)(const char *, fsinfo_t *);
+       void            (*cleanup_options)(fsinfo_t *);
+       void            (*make_fs)(const char *, const char *, fsnode *,
+                               fsinfo_t *);
+} fstype_t;
+
+static fstype_t fstypes[] = {
+#define ENTRY(name) { \
+       # name, name ## _prep_opts, name ## _parse_opts, \
+       name ## _cleanup_opts, name ## _makefs  \
+}
+       ENTRY(ffs),
+       ENTRY(cd9660),
+       ENTRY(chfs),
+       ENTRY(v7fs),
+       ENTRY(msdos),
+       ENTRY(udf),
+       { .type = NULL  },
+};
+
+u_int          debug;
+struct timespec        start_time;
+
+static fstype_t *get_fstype(const char *);
+static void    usage(fstype_t *, fsinfo_t *) __dead;
+
+int
+main(int argc, char *argv[])
+{
+       struct timeval   start;
+       fstype_t        *fstype;
+       fsinfo_t         fsoptions;
+       fsnode          *root;
+       int              ch, i, len;
+       char            *specfile;
+
+       setprogname(argv[0]);
+
+       debug = 0;
+       if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL)
+               errx(1, "Unknown default fs type `%s'.", DEFAULT_FSTYPE);
+
+               /* set default fsoptions */
+       (void)memset(&fsoptions, 0, sizeof(fsoptions));
+       fsoptions.fd = -1;
+       fsoptions.sectorsize = -1;
+
+       if (fstype->prepare_options)
+               fstype->prepare_options(&fsoptions);
+
+       specfile = NULL;
+       if (gettimeofday(&start, NULL) == -1)
+               err(1, "Unable to get system time");
+
+       start_time.tv_sec = start.tv_sec;
+       start_time.tv_nsec = start.tv_usec * 1000;
+
+       while ((ch = getopt(argc, argv, "B:b:d:f:F:M:m:N:O:o:rs:S:t:xZ")) != -1) {
+               switch (ch) {
+
+               case 'B':
+                       if (strcmp(optarg, "be") == 0 ||
+                           strcmp(optarg, "4321") == 0 ||
+                           strcmp(optarg, "big") == 0) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+                               fsoptions.needswap = 1;
+#endif
+                       } else if (strcmp(optarg, "le") == 0 ||
+                           strcmp(optarg, "1234") == 0 ||
+                           strcmp(optarg, "little") == 0) {
+#if BYTE_ORDER == BIG_ENDIAN
+                               fsoptions.needswap = 1;
+#endif
+                       } else {
+                               warnx("Invalid endian `%s'.", optarg);
+                               usage(fstype, &fsoptions);
+                       }
+                       break;
+
+               case 'b':
+                       len = strlen(optarg) - 1;
+                       if (optarg[len] == '%') {
+                               optarg[len] = '\0';
+                               fsoptions.freeblockpc =
+                                   strsuftoll("free block percentage",
+                                       optarg, 0, 99);
+                       } else {
+                               fsoptions.freeblocks =
+                                   strsuftoll("free blocks",
+                                       optarg, 0, LLONG_MAX);
+                       }
+                       break;
+
+               case 'd':
+                       debug = strtoll(optarg, NULL, 0);
+                       break;
+
+               case 'f':
+                       len = strlen(optarg) - 1;
+                       if (optarg[len] == '%') {
+                               optarg[len] = '\0';
+                               fsoptions.freefilepc =
+                                   strsuftoll("free file percentage",
+                                       optarg, 0, 99);
+                       } else {
+                               fsoptions.freefiles =
+                                   strsuftoll("free files",
+                                       optarg, 0, LLONG_MAX);
+                       }
+                       break;
+
+               case 'F':
+                       specfile = optarg;
+                       break;
+
+               case 'M':
+                       fsoptions.minsize =
+                           strsuftoll("minimum size", optarg, 1LL, LLONG_MAX);
+                       break;
+
+               case 'N':
+                       if (! setup_getid(optarg))
+                               errx(1,
+                           "Unable to use user and group databases in `%s'",
+                                   optarg);
+                       break;
+
+               case 'm':
+                       fsoptions.maxsize =
+                           strsuftoll("maximum size", optarg, 1LL, LLONG_MAX);
+                       break;
+
+               case 'O':
+                       fsoptions.offset = 
+                           strsuftoll("offset", optarg, 0LL, LLONG_MAX);
+                       break;
+                       
+               case 'o':
+               {
+                       char *p;
+
+                       while ((p = strsep(&optarg, ",")) != NULL) {
+                               if (*p == '\0')
+                                       errx(1, "Empty option");
+                               if (! fstype->parse_options(p, &fsoptions))
+                                       usage(fstype, &fsoptions);
+                       }
+                       break;
+               }
+
+               case 'r':
+                       fsoptions.replace = 1;
+                       break;
+
+               case 's':
+                       fsoptions.minsize = fsoptions.maxsize =
+                           strsuftoll("size", optarg, 1LL, LLONG_MAX);
+                       break;
+
+               case 'S':
+                       fsoptions.sectorsize =
+                           (int)strsuftoll("sector size", optarg,
+                               1LL, INT_MAX);
+                       break;
+
+               case 't':
+                       /* Check current one and cleanup if necessary. */
+                       if (fstype->cleanup_options)
+                               fstype->cleanup_options(&fsoptions);
+                       fsoptions.fs_specific = NULL;
+                       if ((fstype = get_fstype(optarg)) == NULL)
+                               errx(1, "Unknown fs type `%s'.", optarg);
+                       fstype->prepare_options(&fsoptions);
+                       break;
+
+               case 'x':
+                       fsoptions.onlyspec = 1;
+                       break;
+
+               case 'Z':
+                       fsoptions.sparse = 1;
+                       break;
+
+               case '?':
+               default:
+                       usage(fstype, &fsoptions);
+                       /* NOTREACHED */
+
+               }
+       }
+       if (debug) {
+               printf("debug mask: 0x%08x\n", debug);
+               printf("start time: %ld.%ld, %s",
+                   (long)start_time.tv_sec, (long)start_time.tv_nsec,
+                   ctime(&start_time.tv_sec));
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 2)
+               usage(fstype, &fsoptions);
+
+       /* -x must be accompanied by -F */
+       if (fsoptions.onlyspec != 0 && specfile == NULL)
+               errx(1, "-x requires -F mtree-specfile.");
+
+                               /* walk the tree */
+       TIMER_START(start);
+       root = walk_dir(argv[1], ".", NULL, NULL, fsoptions.replace);
+       TIMER_RESULTS(start, "walk_dir");
+
+       /* append extra directory */
+       for (i = 2; i < argc; i++) {
+               struct stat sb;
+               if (stat(argv[i], &sb) == -1)
+                       err(1, "Can't stat `%s'", argv[i]);
+               if (!S_ISDIR(sb.st_mode))
+                       errx(1, "%s: not a directory", argv[i]);
+               TIMER_START(start);
+               root = walk_dir(argv[i], ".", NULL, root, fsoptions.replace);
+               TIMER_RESULTS(start, "walk_dir2");
+       }
+
+       if (specfile) {         /* apply a specfile */
+               TIMER_START(start);
+               apply_specfile(specfile, argv[1], root, fsoptions.onlyspec);
+               TIMER_RESULTS(start, "apply_specfile");
+       }
+
+       if (debug & DEBUG_DUMP_FSNODES) {
+               printf("\nparent: %s\n", argv[1]);
+               dump_fsnodes(root);
+               putchar('\n');
+       }
+
+                               /* build the file system */
+       TIMER_START(start);
+       fstype->make_fs(argv[0], argv[1], root, &fsoptions);
+       TIMER_RESULTS(start, "make_fs");
+
+       free_fsnodes(root);
+
+       exit(0);
+       /* NOTREACHED */
+}
+
+int
+set_option(const option_t *options, const char *option, char *buf, size_t len)
+{
+       char *var, *val;
+       int retval;
+
+       assert(option != NULL);
+
+       var = estrdup(option);
+       for (val = var; *val; val++)
+               if (*val == '=') {
+                       *val++ = '\0';
+                       break;
+               }
+       retval = set_option_var(options, var, val, buf, len);
+       free(var);
+       return retval;
+}
+
+int
+set_option_var(const option_t *options, const char *var, const char *val,
+    char *buf, size_t len)
+{
+       char *s;
+       size_t i;
+
+#define NUM(type) \
+       if (!*val) { \
+               *(type *)options[i].value = 1; \
+               break; \
+       } \
+       *(type *)options[i].value = (type)strsuftoll(options[i].desc, val, \
+           options[i].minimum, options[i].maximum); break
+
+       for (i = 0; options[i].name != NULL; i++) {
+               if (var[1] == '\0') {
+                       if (options[i].letter != var[0])
+                               continue;
+               } else if (strcmp(options[i].name, var) != 0)
+                       continue;
+               switch (options[i].type) {
+               case OPT_BOOL:
+                       *(bool *)options[i].value = 1;
+                       break;
+               case OPT_STRARRAY:
+                       strlcpy((void *)options[i].value, val, (size_t)
+                           options[i].maximum);
+                       break;
+               case OPT_STRPTR:
+                       s = estrdup(val);
+                       *(char **)options[i].value = s;
+                       break;
+               case OPT_STRBUF:
+                       if (buf == NULL)
+                               abort();
+                       strlcpy(buf, val, len);
+                       break;
+               case OPT_INT64:
+                       NUM(uint64_t);
+               case OPT_INT32:
+                       NUM(uint32_t);
+               case OPT_INT16:
+                       NUM(uint16_t);
+               case OPT_INT8:
+                       NUM(uint8_t);
+               default:
+                       warnx("Unknown type %d in option %s", options[i].type,
+                           val);
+                       return 0;
+               }
+               return i;
+       }
+       warnx("Unknown option `%s'", var);
+       return -1;
+}
+
+
+static fstype_t *
+get_fstype(const char *type)
+{
+       int i;
+       
+       for (i = 0; fstypes[i].type != NULL; i++)
+               if (strcmp(fstypes[i].type, type) == 0)
+                       return (&fstypes[i]);
+       return (NULL);
+}
+
+option_t *
+copy_opts(const option_t *o)
+{
+       size_t i;
+       for (i = 0; o[i].name; i++)
+               continue;
+       i++;
+       return memcpy(ecalloc(i, sizeof(*o)), o, i * sizeof(*o));
+}
+
+static void
+usage(fstype_t *fstype, fsinfo_t *fsoptions)
+{
+       const char *prog;
+
+       prog = getprogname();
+       fprintf(stderr,
+"Usage: %s [-rxZ] [-B endian] [-b free-blocks] [-d debug-mask]\n"
+"\t[-F mtree-specfile] [-f free-files] [-M minimum-size] [-m maximum-size]\n"
+"\t[-N userdb-dir] [-O offset] [-o fs-options] [-S sector-size]\n"
+"\t[-s image-size] [-t fs-type] image-file directory [extra-directory ...]\n",
+           prog);
+
+       if (fstype) {
+               size_t i;
+               option_t *o = fsoptions->fs_options;
+
+               fprintf(stderr, "\n%s specific options:\n", fstype->type);
+               for (i = 0; o[i].name != NULL; i++)
+                       fprintf(stderr, "\t%c%c%20.20s\t%s\n",
+                           o[i].letter ? o[i].letter : ' ',
+                           o[i].letter ? ',' : ' ',
+                           o[i].name, o[i].desc);
+       }
+       exit(1);
+}
diff --git a/usr.sbin/makefs/makefs.h b/usr.sbin/makefs/makefs.h
new file mode 100644 (file)
index 0000000..b856d52
--- /dev/null
@@ -0,0 +1,267 @@
+/*     $NetBSD: makefs.h,v 1.35 2013/08/05 14:41:57 reinoud Exp $      */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#ifndef        _MAKEFS_H
+#define        _MAKEFS_H
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#else
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#define HAVE_STRUCT_STAT_ST_GEN 1
+#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1
+#define HAVE_STRUCT_STATVFS_F_IOSIZE 1
+#define HAVE_STRUCT_STAT_BIRTHTIME 1
+#define HAVE_FSTATVFS 1
+#endif
+
+#include <sys/stat.h>
+#include <err.h>
+
+/*
+ * fsnode -
+ *     a component of the tree; contains a filename, a pointer to
+ *     fsinode, optional symlink name, and tree pointers
+ *
+ * fsinode - 
+ *     equivalent to an inode, containing target file system inode number,
+ *     refcount (nlink), and stat buffer
+ *
+ * A tree of fsnodes looks like this:
+ *
+ *     name    "."             "bin"           "netbsd"
+ *     type    S_IFDIR         S_IFDIR         S_IFREG
+ *     next      >               >             NULL
+ *     parent  NULL            NULL            NULL
+ *     child   NULL              v
+ *
+ *     name                    "."             "ls"
+ *     type                    S_IFDIR         S_IFREG
+ *     next                      >             NULL
+ *     parent                    ^             ^ (to "bin")
+ *     child                   NULL            NULL
+ *
+ * Notes:
+ *     -   first always points to first entry, at current level, which
+ *         must be "." when the tree has been built; during build it may
+ *         not be if "." hasn't yet been found by readdir(2).
+ */
+
+enum fi_flags {
+       FI_SIZED =      1<<0,           /* inode sized */
+       FI_ALLOCATED =  1<<1,           /* fsinode->ino allocated */
+       FI_WRITTEN =    1<<2,           /* inode written */
+};
+
+typedef struct {
+       uint32_t         ino;           /* inode number used on target fs */
+       uint32_t         nlink;         /* number of links to this entry */
+       enum fi_flags    flags;         /* flags used by fs specific code */
+       struct stat      st;            /* stat entry */
+       void            *fsuse;         /* for storing FS dependent info */
+} fsinode;
+
+typedef struct _fsnode {
+       struct _fsnode  *parent;        /* parent (NULL if root) */
+       struct _fsnode  *child;         /* child (if type == S_IFDIR) */
+       struct _fsnode  *next;          /* next */
+       struct _fsnode  *first;         /* first node of current level (".") */
+       uint32_t         type;          /* type of entry */
+       fsinode         *inode;         /* actual inode data */
+       char            *symlink;       /* symlink target */
+       const char      *root;          /* root path */
+       char            *path;          /* directory name */
+       char            *name;          /* file name */
+       int             flags;          /* misc flags */
+} fsnode;
+
+#define        FSNODE_F_HASSPEC        0x01    /* fsnode has a spec entry */
+
+/*
+ * option_t - contains option name, description, pointer to location to store
+ * result, and range checks for the result. Used to simplify fs specific
+ * option setting
+ */
+typedef enum {
+       OPT_STRARRAY,
+       OPT_STRPTR,
+       OPT_STRBUF,
+       OPT_BOOL,
+       OPT_INT8,
+       OPT_INT16,
+       OPT_INT32,
+       OPT_INT64
+} opttype_t;
+
+typedef struct {
+       char            letter;         /* option letter NUL for none */
+       const char      *name;          /* option name */
+       void            *value;         /* where to stuff the value */
+       opttype_t       type;           /* type of entry */
+       long long       minimum;        /* minimum for value */
+       long long       maximum;        /* maximum for value */
+       const char      *desc;          /* option description */
+} option_t;
+
+/*
+ * fsinfo_t - contains various settings and parameters pertaining to
+ * the image, including current settings, global options, and fs
+ * specific options
+ */
+typedef struct makefs_fsinfo {
+               /* current settings */
+       off_t   size;           /* total size */
+       off_t   inodes;         /* number of inodes */
+       uint32_t curinode;      /* current inode */
+
+               /* image settings */
+       int     fd;             /* file descriptor of image */
+       void    *superblock;    /* superblock */
+       int     onlyspec;       /* only add entries in specfile */
+
+
+               /* global options */
+       off_t   minsize;        /* minimum size image should be */
+       off_t   maxsize;        /* maximum size image can be */
+       off_t   freefiles;      /* free file entries to leave */
+       off_t   freeblocks;     /* free blocks to leave */
+       off_t   offset;         /* offset from start of file */
+       int     freefilepc;     /* free file % */
+       int     freeblockpc;    /* free block % */
+       int     needswap;       /* non-zero if byte swapping needed */
+       int     sectorsize;     /* sector size */
+       int     sparse;         /* sparse image, don't fill it with zeros */
+       int     replace;        /* replace files when merging */
+
+       void    *fs_specific;   /* File system specific additions. */
+       option_t *fs_options;   /* File system specific options */
+} fsinfo_t;
+
+
+
+
+void           apply_specfile(const char *, const char *, fsnode *, int);
+void           dump_fsnodes(fsnode *);
+const char *   inode_type(mode_t);
+int            set_option(const option_t *, const char *, char *, size_t);
+int            set_option_var(const option_t *, const char *, const char *,
+    char *, size_t);
+fsnode *       walk_dir(const char *, const char *, fsnode *, fsnode *, int);
+void           free_fsnodes(fsnode *);
+option_t *     copy_opts(const option_t *);
+
+#define DECLARE_FUN(fs)                                                        \
+void           fs ## _prep_opts(fsinfo_t *);                           \
+int            fs ## _parse_opts(const char *, fsinfo_t *);            \
+void           fs ## _cleanup_opts(fsinfo_t *);                        \
+void           fs ## _makefs(const char *, const char *, fsnode *, fsinfo_t *)
+
+DECLARE_FUN(ffs);
+DECLARE_FUN(cd9660);
+DECLARE_FUN(chfs);
+DECLARE_FUN(v7fs);
+DECLARE_FUN(msdos);
+DECLARE_FUN(udf);
+
+extern u_int           debug;
+extern struct timespec start_time;
+
+/*
+ * If -x is specified, we want to exclude nodes which do not appear
+ * in the spec file.
+ */
+#define        FSNODE_EXCLUDE_P(opts, fsnode)  \
+       ((opts)->onlyspec != 0 && ((fsnode)->flags & FSNODE_F_HASSPEC) == 0)
+
+#define        DEBUG_TIME                      0x00000001
+               /* debug bits 1..3 unused at this time */
+#define        DEBUG_WALK_DIR                  0x00000010
+#define        DEBUG_WALK_DIR_NODE             0x00000020
+#define        DEBUG_WALK_DIR_LINKCHECK        0x00000040
+#define        DEBUG_DUMP_FSNODES              0x00000080
+#define        DEBUG_DUMP_FSNODES_VERBOSE      0x00000100
+#define        DEBUG_FS_PARSE_OPTS             0x00000200
+#define        DEBUG_FS_MAKEFS                 0x00000400
+#define        DEBUG_FS_VALIDATE               0x00000800
+#define        DEBUG_FS_CREATE_IMAGE           0x00001000
+#define        DEBUG_FS_SIZE_DIR               0x00002000
+#define        DEBUG_FS_SIZE_DIR_NODE          0x00004000
+#define        DEBUG_FS_SIZE_DIR_ADD_DIRENT    0x00008000
+#define        DEBUG_FS_POPULATE               0x00010000
+#define        DEBUG_FS_POPULATE_DIRBUF        0x00020000
+#define        DEBUG_FS_POPULATE_NODE          0x00040000
+#define        DEBUG_FS_WRITE_FILE             0x00080000
+#define        DEBUG_FS_WRITE_FILE_BLOCK       0x00100000
+#define        DEBUG_FS_MAKE_DIRBUF            0x00200000
+#define        DEBUG_FS_WRITE_INODE            0x00400000
+#define        DEBUG_BUF_BREAD                 0x00800000
+#define        DEBUG_BUF_BWRITE                0x01000000
+#define        DEBUG_BUF_GETBLK                0x02000000
+#define        DEBUG_APPLY_SPECFILE            0x04000000
+#define        DEBUG_APPLY_SPECENTRY           0x08000000
+#define        DEBUG_APPLY_SPECONLY            0x10000000
+
+
+#define        TIMER_START(x)                          \
+       if (debug & DEBUG_TIME)                 \
+               gettimeofday(&(x), NULL)
+
+#define        TIMER_RESULTS(x,d)                              \
+       if (debug & DEBUG_TIME) {                       \
+               struct timeval end, td;                 \
+               gettimeofday(&end, NULL);               \
+               timersub(&end, &(x), &td);              \
+               printf("%s took %lld.%06ld seconds\n",  \
+                   (d), (long long)td.tv_sec,          \
+                   (long)td.tv_usec);                  \
+       }
+
+
+#ifndef        DEFAULT_FSTYPE
+#define        DEFAULT_FSTYPE  "ffs"
+#endif
+
+
+/*
+ *     ffs specific settings
+ *     ---------------------
+ */
+
+#define        FFS_EI          /* for opposite endian support in ffs headers */
+
+
+#endif /* _MAKEFS_H */
diff --git a/usr.sbin/makefs/msdos.c b/usr.sbin/makefs/msdos.c
new file mode 100644 (file)
index 0000000..5a7b103
--- /dev/null
@@ -0,0 +1,257 @@
+/*     $NetBSD: msdos.c,v 1.14 2013/02/03 03:21:21 christos Exp $      */
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: msdos.c,v 1.14 2013/02/03 03:21:21 christos Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/mount.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <util.h>
+
+#include <ffs/buf.h>
+#include <fs/msdosfs/denode.h>
+#include "makefs.h"
+#include "msdos.h"
+#include "mkfs_msdos.h"
+
+static int msdos_populate_dir(const char *, struct denode *, fsnode *,
+    fsnode *, fsinfo_t *);
+
+void
+msdos_prep_opts(fsinfo_t *fsopts)
+{
+       struct msdos_options *msdos_opt = ecalloc(1, sizeof(*msdos_opt));
+       const option_t msdos_options[] = {
+#define AOPT(_opt, _type, _name, _min, _desc) {                        \
+       .letter = _opt,                                                 \
+       .name = # _name,                                                \
+       .type = _min == -1 ? OPT_STRPTR :                               \
+           (_min == -2 ? OPT_BOOL :                                    \
+           (sizeof(_type) == 1 ? OPT_INT8 :                            \
+           (sizeof(_type) == 2 ? OPT_INT16 :                           \
+           (sizeof(_type) == 4 ? OPT_INT32 : OPT_INT64)))),            \
+       .value = &msdos_opt->_name,                                     \
+       .minimum = _min,                                                \
+       .maximum = sizeof(_type) == 1 ? 0xff :                          \
+           (sizeof(_type) == 2 ? 0xffff :                              \
+           (sizeof(_type) == 4 ? 0xffffffff : 0xffffffffffffffffLL)),  \
+       .desc = _desc,                                          \
+},
+ALLOPTS
+#undef AOPT    
+               { .name = NULL }
+       };
+
+       fsopts->fs_specific = msdos_opt;
+       fsopts->fs_options = copy_opts(msdos_options);
+}
+
+void
+msdos_cleanup_opts(fsinfo_t *fsopts)
+{
+       free(fsopts->fs_specific);
+       free(fsopts->fs_options);
+}
+
+int
+msdos_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+       struct msdos_options *msdos_opt = fsopts->fs_specific;
+       option_t *msdos_options = fsopts->fs_options;
+
+       int rv;
+
+       assert(option != NULL);
+       assert(fsopts != NULL);
+       assert(msdos_opt != NULL);
+
+       if (debug & DEBUG_FS_PARSE_OPTS)
+               printf("msdos_parse_opts: got `%s'\n", option);
+
+       rv = set_option(msdos_options, option, NULL, 0);
+       if (rv == -1)
+               return rv;
+
+       if (strcmp(msdos_options[rv].name, "volume_id") == 0)
+               msdos_opt->volume_id_set = 1;
+       else if (strcmp(msdos_options[rv].name, "media_descriptor") == 0)
+               msdos_opt->media_descriptor_set = 1;
+       else if (strcmp(msdos_options[rv].name, "hidden_sectors") == 0)
+               msdos_opt->hidden_sectors_set = 1;
+       return 1;
+}
+
+
+void
+msdos_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+       struct msdos_options *msdos_opt = fsopts->fs_specific;
+       struct vnode vp, rootvp;
+       struct timeval  start;
+       struct msdosfsmount *pmp;
+
+       assert(image != NULL);
+       assert(dir != NULL);
+       assert(root != NULL);
+       assert(fsopts != NULL);
+
+       /*
+        * XXX: pick up other options from the msdos specific ones?
+        * Is minsize right here?
+        */
+       msdos_opt->create_size = MAX(msdos_opt->create_size, fsopts->minsize);
+       msdos_opt->offset = fsopts->offset;
+       if (msdos_opt->bytes_per_sector == 0) {
+               if (fsopts->sectorsize == -1)
+                       fsopts->sectorsize = 512;
+               msdos_opt->bytes_per_sector = fsopts->sectorsize;
+       } else if (fsopts->sectorsize == -1) {
+               fsopts->sectorsize = msdos_opt->bytes_per_sector;
+       } else if (fsopts->sectorsize != msdos_opt->bytes_per_sector) {
+               err(1, "inconsistent sectorsize -S %u"
+                   "!= -o bytes_per_sector %u", 
+                   fsopts->sectorsize, msdos_opt->bytes_per_sector);
+       }
+
+               /* create image */
+       printf("Creating `%s'\n", image);
+       TIMER_START(start);
+       if (mkfs_msdos(image, NULL, msdos_opt) == -1)
+               return;
+       TIMER_RESULTS(start, "mkfs_msdos");
+
+       fsopts->fd = open(image, O_RDWR);
+       vp.fs = fsopts;
+
+       if ((pmp = msdosfs_mount(&vp, 0)) == NULL)
+               err(1, "msdosfs_mount");
+
+       if (msdosfs_root(pmp, &rootvp) != 0)
+               err(1, "msdosfs_root");
+
+       if (debug & DEBUG_FS_MAKEFS)
+               printf("msdos_makefs: image %s directory %s root %p\n",
+                   image, dir, root);
+
+               /* populate image */
+       printf("Populating `%s'\n", image);
+       TIMER_START(start);
+       if (msdos_populate_dir(dir, VTODE(&rootvp), root, root, fsopts) == -1)
+               errx(1, "Image file `%s' not created.", image);
+       TIMER_RESULTS(start, "msdos_populate_dir");
+
+       if (debug & DEBUG_FS_MAKEFS)
+               putchar('\n');
+
+               /* ensure no outstanding buffers remain */
+       if (debug & DEBUG_FS_MAKEFS)
+               bcleanup();
+
+       printf("Image `%s' complete\n", image);
+}
+
+static int
+msdos_populate_dir(const char *path, struct denode *dir, fsnode *root,
+    fsnode *parent, fsinfo_t *fsopts)
+{
+       fsnode *cur;
+       char pbuf[MAXPATHLEN];
+
+       assert(dir != NULL);
+       assert(root != NULL);
+       assert(fsopts != NULL); 
+       
+       for (cur = root->next; cur != NULL; cur = cur->next) {
+               if ((size_t)snprintf(pbuf, sizeof(pbuf), "%s/%s", path,
+                   cur->name) >= sizeof(pbuf)) {
+                       warnx("path %s too long", pbuf);
+                       return -1;
+               }
+
+               if ((cur->inode->flags & FI_ALLOCATED) == 0) {
+                       cur->inode->flags |= FI_ALLOCATED;
+                       if (cur != root) {
+                               fsopts->curinode++;
+                               cur->inode->ino = fsopts->curinode;
+                               cur->parent = parent;
+                       }
+               }
+
+               if (cur->inode->flags & FI_WRITTEN) {
+                       continue;       // hard link
+               }
+               cur->inode->flags |= FI_WRITTEN;
+
+               if (cur->child) {
+                       struct denode *de;
+                       if ((de = msdosfs_mkdire(pbuf, dir, cur)) == NULL) {
+                               warn("msdosfs_mkdire %s", pbuf);
+                               return -1;
+                       }
+                       if (msdos_populate_dir(pbuf, de, cur->child, cur,
+                           fsopts) == -1) {
+                               warn("msdos_populate_dir %s", pbuf);
+                               return -1;
+                       }
+                       continue;
+               } else if (!S_ISREG(cur->type)) {
+                       warnx("skipping non-regular file %s/%s", cur->path,
+                           cur->name);
+                       continue;
+               }
+               if (msdosfs_mkfile(pbuf, dir, cur) == NULL) {
+                       warn("msdosfs_mkfile %s", pbuf);
+                       return -1;
+               }
+       }
+       return 0;
+}
diff --git a/usr.sbin/makefs/msdos.h b/usr.sbin/makefs/msdos.h
new file mode 100644 (file)
index 0000000..3571bef
--- /dev/null
@@ -0,0 +1,42 @@
+/*     $NetBSD: msdos.h,v 1.2 2013/01/26 00:31:49 christos Exp $       */
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+struct vnode;
+struct denode;
+
+struct msdosfsmount *msdosfs_mount(struct vnode *, int);
+int msdosfs_root(struct msdosfsmount *, struct vnode *);
+
+struct denode *msdosfs_mkfile(const char *, struct denode *, fsnode *);
+struct denode *msdosfs_mkdire(const char *, struct denode *, fsnode *);
diff --git a/usr.sbin/makefs/msdos/Makefile.inc b/usr.sbin/makefs/msdos/Makefile.inc
new file mode 100644 (file)
index 0000000..fbcce2a
--- /dev/null
@@ -0,0 +1,15 @@
+#      $NetBSD: Makefile.inc,v 1.5 2013/01/26 16:50:46 christos Exp $
+#
+
+MSDOS= ${NETBSDSRCDIR}/sys/fs/msdosfs
+MSDOS_NEWFS=   ${NETBSDSRCDIR}/sbin/newfs_msdos
+
+.PATH: ${.CURDIR}/msdos ${MSDOS} ${MSDOS_NEWFS}
+
+CPPFLAGS+= -DMSDOS_EI -I${MSDOS} -I${MSDOS_NEWFS}
+.if !defined(HOSTPROGNAME)
+CPPFLAGS+= -I${NETBSDSRCDIR}/sys
+.endif
+
+SRCS+= mkfs_msdos.c msdosfs_fat.c msdosfs_conv.c msdosfs_vfsops.c
+SRCS+= msdosfs_lookup.c msdosfs_denode.c msdosfs_vnops.c
diff --git a/usr.sbin/makefs/msdos/msdosfs_denode.c b/usr.sbin/makefs/msdos/msdosfs_denode.c
new file mode 100644 (file)
index 0000000..47ebee0
--- /dev/null
@@ -0,0 +1,363 @@
+/*     $NetBSD: msdosfs_denode.c,v 1.6 2013/10/19 17:16:37 christos Exp $      */
+
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.6 2013/10/19 17:16:37 christos Exp $");
+
+#include <sys/param.h>
+
+#include <ffs/buf.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/fat.h>
+
+#include <util.h>
+
+/*
+ * If deget() succeeds it returns with the gotten denode locked().
+ *
+ * pmp      - address of msdosfsmount structure of the filesystem containing
+ *            the denode of interest.  The pm_dev field and the address of
+ *            the msdosfsmount structure are used.
+ * dirclust  - which cluster bp contains, if dirclust is 0 (root directory)
+ *            diroffset is relative to the beginning of the root directory,
+ *            otherwise it is cluster relative.
+ * diroffset - offset past begin of cluster of denode we want
+ * depp             - returns the address of the gotten denode.
+ */
+int
+deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
+    struct denode **depp)
+       /* pmp:  so we know the maj/min number */
+       /* dirclust:             cluster this dir entry came from */
+       /* diroffset:            index of entry within the cluster */
+       /* depp:                 returns the addr of the gotten denode */
+{
+       int error;
+       struct direntry *direntptr;
+       struct denode *ldep;
+       struct buf *bp;
+
+#ifdef MSDOSFS_DEBUG
+       printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
+           pmp, dirclust, diroffset, depp);
+#endif
+
+       /*
+        * On FAT32 filesystems, root is a (more or less) normal
+        * directory
+        */
+       if (FAT32(pmp) && dirclust == MSDOSFSROOT)
+               dirclust = pmp->pm_rootdirblk;
+
+       ldep = ecalloc(1, sizeof(*ldep));
+       ldep->de_vnode = NULL;
+       ldep->de_flag = 0;
+       ldep->de_devvp = 0;
+       ldep->de_lockf = 0;
+       ldep->de_dev = pmp->pm_dev;
+       ldep->de_dirclust = dirclust;
+       ldep->de_diroffset = diroffset;
+       ldep->de_pmp = pmp;
+       ldep->de_devvp = pmp->pm_devvp;
+       ldep->de_refcnt = 1;
+       fc_purge(ldep, 0);
+       /*
+        * Copy the directory entry into the denode area of the vnode.
+        */
+       if ((dirclust == MSDOSFSROOT
+            || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
+           && diroffset == MSDOSFSROOT_OFS) {
+               /*
+                * Directory entry for the root directory. There isn't one,
+                * so we manufacture one. We should probably rummage
+                * through the root directory and find a label entry (if it
+                * exists), and then use the time and date from that entry
+                * as the time and date for the root denode.
+                */
+               ldep->de_vnode = (struct vnode *)-1;
+
+               ldep->de_Attributes = ATTR_DIRECTORY;
+               if (FAT32(pmp))
+                       ldep->de_StartCluster = pmp->pm_rootdirblk;
+                       /* de_FileSize will be filled in further down */
+               else {
+                       ldep->de_StartCluster = MSDOSFSROOT;
+                       ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
+               }
+               /*
+                * fill in time and date so that dos2unixtime() doesn't
+                * spit up when called from msdosfs_getattr() with root
+                * denode
+                */
+               ldep->de_CHun = 0;
+               ldep->de_CTime = 0x0000;        /* 00:00:00      */
+               ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
+                   | (1 << DD_DAY_SHIFT);
+               /* Jan 1, 1980   */
+               ldep->de_ADate = ldep->de_CDate;
+               ldep->de_MTime = ldep->de_CTime;
+               ldep->de_MDate = ldep->de_CDate;
+               /* leave the other fields as garbage */
+       } else {
+               error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
+               if (error) {
+                       ldep->de_devvp = NULL;
+                       ldep->de_Name[0] = SLOT_DELETED;
+                       return (error);
+               }
+               DE_INTERNALIZE(ldep, direntptr);
+               brelse(bp, 0);
+       }
+
+       /*
+        * Fill in a few fields of the vnode and finish filling in the
+        * denode.  Then return the address of the found denode.
+        */
+       if (ldep->de_Attributes & ATTR_DIRECTORY) {
+               /*
+                * Since DOS directory entries that describe directories
+                * have 0 in the filesize field, we take this opportunity
+                * to find out the length of the directory and plug it into
+                * the denode structure.
+                */
+               u_long size;
+
+               if (ldep->de_StartCluster != MSDOSFSROOT) {
+                       error = pcbmap(ldep, CLUST_END, 0, &size, 0);
+                       if (error == E2BIG) {
+                               ldep->de_FileSize = de_cn2off(pmp, size);
+                               error = 0;
+                       } else
+                               printf("deget(): pcbmap returned %d\n", error);
+               }
+       }
+       *depp = ldep;
+       return (0);
+}
+
+/*
+ * Truncate the file described by dep to the length specified by length.
+ */
+int
+detrunc(struct denode *dep, u_long length, int flags, struct kauth_cred *cred)
+{
+       int error;
+       int allerror = 0;
+       u_long eofentry;
+       u_long chaintofree = 0;
+       daddr_t bn, lastblock;
+       int boff;
+       int isadir = dep->de_Attributes & ATTR_DIRECTORY;
+       struct buf *bp;
+       struct msdosfsmount *pmp = dep->de_pmp;
+
+#ifdef MSDOSFS_DEBUG
+       printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags);
+#endif
+
+       /*
+        * Disallow attempts to truncate the root directory since it is of
+        * fixed size.  That's just the way dos filesystems are.  We use
+        * the VROOT bit in the vnode because checking for the directory
+        * bit and a startcluster of 0 in the denode is not adequate to
+        * recognize the root directory at this point in a file or
+        * directory's life.
+        */
+       if (dep->de_vnode != NULL && !FAT32(pmp)) {
+               printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n",
+                   dep->de_dirclust, dep->de_diroffset);
+               return (EINVAL);
+       }
+
+       if (dep->de_FileSize < length)
+               return (deextend(dep, length, cred));
+       lastblock = de_clcount(pmp, length) - 1;
+
+       /*
+        * If the desired length is 0 then remember the starting cluster of
+        * the file and set the StartCluster field in the directory entry
+        * to 0.  If the desired length is not zero, then get the number of
+        * the last cluster in the shortened file.  Then get the number of
+        * the first cluster in the part of the file that is to be freed.
+        * Then set the next cluster pointer in the last cluster of the
+        * file to CLUST_EOFE.
+        */
+       if (length == 0) {
+               chaintofree = dep->de_StartCluster;
+               dep->de_StartCluster = 0;
+               eofentry = ~0;
+       } else {
+               error = pcbmap(dep, lastblock, 0, &eofentry, 0);
+               if (error) {
+#ifdef MSDOSFS_DEBUG
+                       printf("detrunc(): pcbmap fails %d\n", error);
+#endif
+                       return (error);
+               }
+       }
+
+       /*
+        * If the new length is not a multiple of the cluster size then we
+        * must zero the tail end of the new last cluster in case it
+        * becomes part of the file again because of a seek.
+        */
+       if ((boff = length & pmp->pm_crbomask) != 0) {
+               if (isadir) {
+                       bn = cntobn(pmp, eofentry);
+                       error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
+                           pmp->pm_bpcluster, NOCRED, B_MODIFY, &bp);
+                       if (error) {
+#ifdef MSDOSFS_DEBUG
+                               printf("detrunc(): bread fails %d\n", error);
+#endif
+                               return (error);
+                       }
+                       memset((char *)bp->b_data + boff, 0,
+                           pmp->pm_bpcluster - boff);
+                       if (flags & IO_SYNC)
+                               bwrite(bp);
+                       else
+                               bdwrite(bp);
+               }
+       }
+
+       /*
+        * Write out the updated directory entry.  Even if the update fails
+        * we free the trailing clusters.
+        */
+       dep->de_FileSize = length;
+       if (!isadir)
+               dep->de_flag |= DE_UPDATE|DE_MODIFIED;
+#ifdef MSDOSFS_DEBUG
+       printf("detrunc(): allerror %d, eofentry %lu\n",
+              allerror, eofentry);
+#endif
+
+       /*
+        * If we need to break the cluster chain for the file then do it
+        * now.
+        */
+       if (eofentry != (u_long)~0) {
+               error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
+                                &chaintofree, CLUST_EOFE);
+               if (error) {
+#ifdef MSDOSFS_DEBUG
+                       printf("detrunc(): fatentry errors %d\n", error);
+#endif
+                       return (error);
+               }
+       }
+
+       /*
+        * Now free the clusters removed from the file because of the
+        * truncation.
+        */
+       if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask))
+               freeclusterchain(pmp, chaintofree);
+
+       return (allerror);
+}
+
+/*
+ * Extend the file described by dep to length specified by length.
+ */
+int
+deextend(struct denode *dep, u_long length, struct kauth_cred *cred)
+{
+       struct msdosfsmount *pmp = dep->de_pmp;
+       u_long count;
+       int error;
+
+       /*
+        * The root of a DOS filesystem cannot be extended.
+        */
+       if (dep->de_vnode != NULL && !FAT32(pmp))
+               return EINVAL;
+
+       /*
+        * Directories cannot be extended.
+        */
+       if (dep->de_Attributes & ATTR_DIRECTORY)
+               return EISDIR;
+
+       if (length <= dep->de_FileSize)
+               return E2BIG;
+
+       /*
+        * Compute the number of clusters to allocate.
+        */
+       count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize);
+       if (count > 0) {
+               if (count > pmp->pm_freeclustercount)
+                       return (ENOSPC);
+               error = extendfile(dep, count, NULL, NULL, DE_CLEAR);
+               if (error) {
+                       /* truncate the added clusters away again */
+                       (void) detrunc(dep, dep->de_FileSize, 0, cred);
+                       return (error);
+               }
+       }
+
+       /*
+        * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a
+        * memset(); we set the write size so ubc won't read in file data that
+        * is zero'd later.
+        */
+       dep->de_FileSize = length;
+       dep->de_flag |= DE_UPDATE|DE_MODIFIED;
+       return 0;
+}
diff --git a/usr.sbin/makefs/msdos/msdosfs_vfsops.c b/usr.sbin/makefs/msdos/msdosfs_vfsops.c
new file mode 100644 (file)
index 0000000..29461c7
--- /dev/null
@@ -0,0 +1,425 @@
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.7 2013/01/30 19:19:19 christos Exp $");
+
+#include <sys/param.h>
+
+#include <ffs/buf.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/bootsect.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/fat.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "msdos.h"
+#include "mkfs_msdos.h"
+
+#ifdef MSDOSFS_DEBUG
+#define DPRINTF(a) printf a
+#else
+#define DPRINTF(a)
+#endif
+
+struct msdosfsmount *
+msdosfs_mount(struct vnode *devvp, int flags)
+{
+       struct msdosfsmount *pmp = NULL;
+       struct buf *bp;
+       union bootsector *bsp;
+       struct byte_bpb33 *b33;
+       struct byte_bpb50 *b50;
+       struct byte_bpb710 *b710;
+       uint8_t SecPerClust;
+       int     ronly = 0, error, tmp;
+       int     bsize;
+       struct msdos_options *m = devvp->fs->fs_specific;
+       uint64_t psize = m->create_size;
+       unsigned secsize = 512;
+
+       DPRINTF(("%s(bread 0)\n", __func__));
+       if ((error = bread(devvp, 0, secsize, NULL, 0, &bp)) != 0)
+               goto error_exit;
+
+       bsp = (union bootsector *)bp->b_data;
+       b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
+       b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
+       b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
+
+       if (!(flags & MSDOSFSMNT_GEMDOSFS)) {
+               if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
+                   || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
+                       DPRINTF(("bootsig0 %d bootsig1 %d\n", 
+                           bsp->bs50.bsBootSectSig0,
+                           bsp->bs50.bsBootSectSig1));
+                       error = EINVAL;
+                       goto error_exit;
+               }
+               bsize = 0;
+       } else
+               bsize = 512;
+
+       pmp = ecalloc(1, sizeof *pmp);
+       /*
+        * Compute several useful quantities from the bpb in the
+        * bootsector.  Copy in the dos 5 variant of the bpb then fix up
+        * the fields that are different between dos 5 and dos 3.3.
+        */
+       SecPerClust = b50->bpbSecPerClust;
+       pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
+       pmp->pm_ResSectors = getushort(b50->bpbResSectors);
+       pmp->pm_FATs = b50->bpbFATs;
+       pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
+       pmp->pm_Sectors = getushort(b50->bpbSectors);
+       pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
+       pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
+       pmp->pm_Heads = getushort(b50->bpbHeads);
+       pmp->pm_Media = b50->bpbMedia;
+
+       DPRINTF(("%s(BytesPerSec=%u, ResSectors=%u, FATs=%d, RootDirEnts=%u, "
+           "Sectors=%u, FATsecs=%lu, SecPerTrack=%u, Heads=%u, Media=%u)\n",
+           __func__, pmp->pm_BytesPerSec, pmp->pm_ResSectors, pmp->pm_FATs,
+           pmp->pm_RootDirEnts, pmp->pm_Sectors, pmp->pm_FATsecs,
+           pmp->pm_SecPerTrack, pmp->pm_Heads, pmp->pm_Media));
+       if (!(flags & MSDOSFSMNT_GEMDOSFS)) {
+               /* XXX - We should probably check more values here */
+               if (!pmp->pm_BytesPerSec || !SecPerClust
+                       || pmp->pm_SecPerTrack > 63) {
+                       DPRINTF(("bytespersec %d secperclust %d "
+                           "secpertrack %d\n", 
+                           pmp->pm_BytesPerSec, SecPerClust,
+                           pmp->pm_SecPerTrack));
+                       error = EINVAL;
+                       goto error_exit;
+               }
+       }
+
+       if (pmp->pm_Sectors == 0) {
+               pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
+               pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
+       } else {
+               pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
+               pmp->pm_HugeSectors = pmp->pm_Sectors;
+       }
+
+       if (pmp->pm_RootDirEnts == 0) {
+               unsigned short vers = getushort(b710->bpbFSVers);
+               /*
+                * Some say that bsBootSectSig[23] must be zero, but
+                * Windows does not require this and some digital cameras
+                * do not set these to zero.  Therefore, do not insist.
+                */
+               if (pmp->pm_Sectors || pmp->pm_FATsecs || vers) {
+                       DPRINTF(("sectors %d fatsecs %lu vers %d\n",
+                           pmp->pm_Sectors, pmp->pm_FATsecs, vers));
+                       error = EINVAL;
+                       goto error_exit;
+               }
+               pmp->pm_fatmask = FAT32_MASK;
+               pmp->pm_fatmult = 4;
+               pmp->pm_fatdiv = 1;
+               pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
+
+               /* mirrorring is enabled if the FATMIRROR bit is not set */
+               if ((getushort(b710->bpbExtFlags) & FATMIRROR) == 0)
+                       pmp->pm_flags |= MSDOSFS_FATMIRROR;
+               else
+                       pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
+       } else
+               pmp->pm_flags |= MSDOSFS_FATMIRROR;
+
+       if (flags & MSDOSFSMNT_GEMDOSFS) {
+               if (FAT32(pmp)) {
+                       DPRINTF(("FAT32 for GEMDOS\n"));
+                       /*
+                        * GEMDOS doesn't know FAT32.
+                        */
+                       error = EINVAL;
+                       goto error_exit;
+               }
+
+               /*
+                * Check a few values (could do some more):
+                * - logical sector size: power of 2, >= block size
+                * - sectors per cluster: power of 2, >= 1
+                * - number of sectors:   >= 1, <= size of partition
+                */
+               if ( (SecPerClust == 0)
+                 || (SecPerClust & (SecPerClust - 1))
+                 || (pmp->pm_BytesPerSec < bsize)
+                 || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1))
+                 || (pmp->pm_HugeSectors == 0)
+                 || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize)
+                     > psize)) {
+                       DPRINTF(("consistency checks for GEMDOS\n"));
+                       error = EINVAL;
+                       goto error_exit;
+               }
+               /*
+                * XXX - Many parts of the msdosfs driver seem to assume that
+                * the number of bytes per logical sector (BytesPerSec) will
+                * always be the same as the number of bytes per disk block
+                * Let's pretend it is.
+                */
+               tmp = pmp->pm_BytesPerSec / bsize;
+               pmp->pm_BytesPerSec  = bsize;
+               pmp->pm_HugeSectors *= tmp;
+               pmp->pm_HiddenSects *= tmp;
+               pmp->pm_ResSectors  *= tmp;
+               pmp->pm_Sectors     *= tmp;
+               pmp->pm_FATsecs     *= tmp;
+               SecPerClust         *= tmp;
+       }
+
+       /* Check that fs has nonzero FAT size */
+       if (pmp->pm_FATsecs == 0) {
+               DPRINTF(("FATsecs is 0\n"));
+               error = EINVAL;
+               goto error_exit;
+       }
+
+       pmp->pm_fatblk = pmp->pm_ResSectors;
+       if (FAT32(pmp)) {
+               pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
+               pmp->pm_firstcluster = pmp->pm_fatblk
+                       + (pmp->pm_FATs * pmp->pm_FATsecs);
+               pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
+       } else {
+               pmp->pm_rootdirblk = pmp->pm_fatblk +
+                       (pmp->pm_FATs * pmp->pm_FATsecs);
+               pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
+                                      + pmp->pm_BytesPerSec - 1)
+                       / pmp->pm_BytesPerSec;/* in sectors */
+               pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
+       }
+
+       pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
+           SecPerClust;
+       pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
+       pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
+
+       if (flags & MSDOSFSMNT_GEMDOSFS) {
+               if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) {
+                       pmp->pm_fatmask = FAT12_MASK;
+                       pmp->pm_fatmult = 3;
+                       pmp->pm_fatdiv = 2;
+               } else {
+                       pmp->pm_fatmask = FAT16_MASK;
+                       pmp->pm_fatmult = 2;
+                       pmp->pm_fatdiv = 1;
+               }
+       } else if (pmp->pm_fatmask == 0) {
+               if (pmp->pm_maxcluster
+                   <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
+                       /*
+                        * This will usually be a floppy disk. This size makes
+                        * sure that one FAT entry will not be split across
+                        * multiple blocks.
+                        */
+                       pmp->pm_fatmask = FAT12_MASK;
+                       pmp->pm_fatmult = 3;
+                       pmp->pm_fatdiv = 2;
+               } else {
+                       pmp->pm_fatmask = FAT16_MASK;
+                       pmp->pm_fatmult = 2;
+                       pmp->pm_fatdiv = 1;
+               }
+       }
+       if (FAT12(pmp))
+               pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
+       else
+               pmp->pm_fatblocksize = MAXBSIZE;
+
+       pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
+       pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
+
+       /*
+        * Compute mask and shift value for isolating cluster relative byte
+        * offsets and cluster numbers from a file offset.
+        */
+       pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
+       pmp->pm_crbomask = pmp->pm_bpcluster - 1;
+       pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
+
+       DPRINTF(("%s(fatmask=%lu, fatmult=%u, fatdiv=%u, fatblocksize=%lu, "
+           "fatblocksec=%lu, bnshift=%lu, pbcluster=%lu, crbomask=%lu, "
+           "cnshift=%lu)\n",
+           __func__, pmp->pm_fatmask, pmp->pm_fatmult, pmp->pm_fatdiv,
+           pmp->pm_fatblocksize, pmp->pm_fatblocksec, pmp->pm_bnshift,
+           pmp->pm_bpcluster, pmp->pm_crbomask, pmp->pm_cnshift));
+       /*
+        * Check for valid cluster size
+        * must be a power of 2
+        */
+       if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
+               DPRINTF(("bpcluster %lu cnshift %lu\n", 
+                   pmp->pm_bpcluster, pmp->pm_cnshift));
+               error = EINVAL;
+               goto error_exit;
+       }
+
+       /*
+        * Release the bootsector buffer.
+        */
+       brelse(bp, BC_AGE);
+       bp = NULL;
+
+       /*
+        * Check FSInfo.
+        */
+       if (pmp->pm_fsinfo) {
+               struct fsinfo *fp;
+
+               /*
+                * XXX  If the fsinfo block is stored on media with
+                *      2KB or larger sectors, is the fsinfo structure
+                *      padded at the end or in the middle?
+                */
+               DPRINTF(("%s(bread %lu)\n", __func__,
+                   (unsigned long)de_bn2kb(pmp, pmp->pm_fsinfo)));
+               if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
+                   pmp->pm_BytesPerSec, NULL, 0, &bp)) != 0)
+                       goto error_exit;
+               fp = (struct fsinfo *)bp->b_data;
+               if (!memcmp(fp->fsisig1, "RRaA", 4)
+                   && !memcmp(fp->fsisig2, "rrAa", 4)
+                   && !memcmp(fp->fsisig3, "\0\0\125\252", 4)
+                   && !memcmp(fp->fsisig4, "\0\0\125\252", 4))
+                       pmp->pm_nxtfree = getulong(fp->fsinxtfree);
+               else
+                       pmp->pm_fsinfo = 0;
+               brelse(bp, 0);
+               bp = NULL;
+       }
+
+       /*
+        * Check and validate (or perhaps invalidate?) the fsinfo structure?
+        * XXX
+        */
+       if (pmp->pm_fsinfo) {
+               if ((pmp->pm_nxtfree == 0xffffffffUL) ||
+                   (pmp->pm_nxtfree > pmp->pm_maxcluster))
+                       pmp->pm_fsinfo = 0;
+       }
+
+       /*
+        * Allocate memory for the bitmap of allocated clusters, and then
+        * fill it in.
+        */
+       pmp->pm_inusemap = ecalloc(sizeof(*pmp->pm_inusemap),
+           ((pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS));
+       /*
+        * fillinusemap() needs pm_devvp.
+        */
+       pmp->pm_dev = 0;
+       pmp->pm_devvp = devvp;
+
+       /*
+        * Have the inuse map filled in.
+        */
+       if ((error = fillinusemap(pmp)) != 0) {
+               DPRINTF(("fillinusemap %d\n", error));
+               goto error_exit;
+       }
+
+       /*
+        * Finish up.
+        */
+       if (ronly)
+               pmp->pm_flags |= MSDOSFSMNT_RONLY;
+       else
+               pmp->pm_fmod = 1;
+
+       /*
+        * If we ever do quotas for DOS filesystems this would be a place
+        * to fill in the info in the msdosfsmount structure. You dolt,
+        * quotas on dos filesystems make no sense because files have no
+        * owners on dos filesystems. of course there is some empty space
+        * in the directory entry where we could put uid's and gid's.
+        */
+
+       return pmp;
+
+error_exit:
+       if (bp)
+               brelse(bp, BC_AGE);
+       if (pmp) {
+               if (pmp->pm_inusemap)
+                       free(pmp->pm_inusemap);
+               free(pmp);
+       }
+       errno = error;
+       return pmp;
+}
+
+int
+msdosfs_root(struct msdosfsmount *pmp, struct vnode *vp) {
+       struct denode *ndep;
+       int error;
+
+       *vp = *pmp->pm_devvp;
+       if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep)) != 0) {
+               errno = error;
+               return -1;
+       }
+       vp->v_data = ndep;
+       return 0;
+}
diff --git a/usr.sbin/makefs/msdos/msdosfs_vnops.c b/usr.sbin/makefs/msdos/msdosfs_vnops.c
new file mode 100644 (file)
index 0000000..53c438c
--- /dev/null
@@ -0,0 +1,639 @@
+/*     $NetBSD: msdosfs_vnops.c,v 1.15 2013/10/19 17:16:37 christos Exp $ */
+
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.15 2013/10/19 17:16:37 christos Exp $");
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <ffs/buf.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/fat.h>
+
+#include "makefs.h"
+#include "msdos.h"
+
+#ifdef MSDOSFS_DEBUG
+#define DPRINTF(a) printf a
+#else
+#define DPRINTF(a)
+#endif
+/*
+ * Some general notes:
+ *
+ * In the ufs filesystem the inodes, superblocks, and indirect blocks are
+ * read/written using the vnode for the filesystem. Blocks that represent
+ * the contents of a file are read/written using the vnode for the file
+ * (including directories when they are read/written as files). This
+ * presents problems for the dos filesystem because data that should be in
+ * an inode (if dos had them) resides in the directory itself. Since we
+ * must update directory entries without the benefit of having the vnode
+ * for the directory we must use the vnode for the filesystem. This means
+ * that when a directory is actually read/written (via read, write, or
+ * readdir, or seek) we must use the vnode for the filesystem instead of
+ * the vnode for the directory as would happen in ufs. This is to insure we
+ * retrieve the correct block from the buffer cache since the hash value is
+ * based upon the vnode address and the desired block number.
+ */
+
+static int msdosfs_wfile(const char *, struct denode *, fsnode *);
+
+static void
+msdosfs_times(struct msdosfsmount *pmp, struct denode *dep,
+    const struct stat *st)
+{
+#ifndef HAVE_NBTOOL_CONFIG_H
+       struct timespec at = st->st_atimespec;
+       struct timespec mt = st->st_mtimespec;
+#else
+       struct timespec at = { st->st_atime, 0 };
+       struct timespec mt = { st->st_mtime, 0 };
+#endif
+       unix2dostime(&at, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL);
+       unix2dostime(&mt, pmp->pm_gmtoff, &dep->de_MDate, &dep->de_MTime, NULL);
+}
+
+/*
+ * When we search a directory the blocks containing directory entries are
+ * read and examined.  The directory entries contain information that would
+ * normally be in the inode of a unix filesystem.  This means that some of
+ * a directory's contents may also be in memory resident denodes (sort of
+ * an inode).  This can cause problems if we are searching while some other
+ * process is modifying a directory.  To prevent one process from accessing
+ * incompletely modified directory information we depend upon being the
+ * sole owner of a directory block.  bread/brelse provide this service.
+ * This being the case, when a process modifies a directory it must first
+ * acquire the disk block that contains the directory entry to be modified.
+ * Then update the disk block and the denode, and then write the disk block
+ * out to disk.         This way disk blocks containing directory entries and in
+ * memory denode's will be in synch.
+ */
+static int
+msdosfs_findslot(struct denode *dp, struct componentname *cnp) 
+{
+       daddr_t bn;
+       int error;
+       int slotcount;
+       int slotoffset = 0;
+       int frcn;
+       u_long cluster;
+       int blkoff;
+       u_int diroff;
+       int blsize;
+       struct msdosfsmount *pmp;
+       struct buf *bp = 0;
+       struct direntry *dep;
+       u_char dosfilename[12];
+       int wincnt = 1;
+       int chksum = -1, chksum_ok;
+       int olddos = 1;
+
+       pmp = dp->de_pmp;
+
+       switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename,
+           cnp->cn_namelen, 0)) {
+       case 0:
+               return (EINVAL);
+       case 1:
+               break;
+       case 2:
+               wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,
+                   cnp->cn_namelen) + 1;
+               break;
+       case 3:
+               olddos = 0;
+               wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,
+                   cnp->cn_namelen) + 1;
+               break;
+       }
+
+       if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
+               wincnt = 1;
+
+       /*
+        * Suppress search for slots unless creating
+        * file and at end of pathname, in which case
+        * we watch for a place to put the new file in
+        * case it doesn't already exist.
+        */
+       slotcount = 0;
+       DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename));
+       /*
+        * Search the directory pointed at by vdp for the name pointed at
+        * by cnp->cn_nameptr.
+        */
+       /*
+        * The outer loop ranges over the clusters that make up the
+        * directory.  Note that the root directory is different from all
+        * other directories.  It has a fixed number of blocks that are not
+        * part of the pool of allocatable clusters.  So, we treat it a
+        * little differently. The root directory starts at "cluster" 0.
+        */
+       diroff = 0;
+       for (frcn = 0; diroff < dp->de_FileSize; frcn++) {
+               if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) {
+                       if (error == E2BIG)
+                               break;
+                       return (error);
+               }
+               error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, NOCRED,
+                   0, &bp);
+               if (error) {
+                       return (error);
+               }
+               for (blkoff = 0; blkoff < blsize;
+                    blkoff += sizeof(struct direntry),
+                    diroff += sizeof(struct direntry)) {
+                       dep = (struct direntry *)((char *)bp->b_data + blkoff);
+                       /*
+                        * If the slot is empty and we are still looking
+                        * for an empty then remember this one.  If the
+                        * slot is not empty then check to see if it
+                        * matches what we are looking for.  If the slot
+                        * has never been filled with anything, then the
+                        * remainder of the directory has never been used,
+                        * so there is no point in searching it.
+                        */
+                       if (dep->deName[0] == SLOT_EMPTY ||
+                           dep->deName[0] == SLOT_DELETED) {
+                               /*
+                                * Drop memory of previous long matches
+                                */
+                               chksum = -1;
+
+                               if (slotcount < wincnt) {
+                                       slotcount++;
+                                       slotoffset = diroff;
+                               }
+                               if (dep->deName[0] == SLOT_EMPTY) {
+                                       brelse(bp, 0);
+                                       goto notfound;
+                               }
+                       } else {
+                               /*
+                                * If there wasn't enough space for our
+                                * winentries, forget about the empty space
+                                */
+                               if (slotcount < wincnt)
+                                       slotcount = 0;
+
+                               /*
+                                * Check for Win95 long filename entry
+                                */
+                               if (dep->deAttributes == ATTR_WIN95) {
+                                       if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
+                                               continue;
+
+                                       chksum = winChkName((const u_char *)cnp->cn_nameptr,
+                                                           cnp->cn_namelen,
+                                                           (struct winentry *)dep,
+                                                           chksum);
+                                       continue;
+                               }
+
+                               /*
+                                * Ignore volume labels (anywhere, not just
+                                * the root directory).
+                                */
+                               if (dep->deAttributes & ATTR_VOLUME) {
+                                       chksum = -1;
+                                       continue;
+                               }
+
+                               /*
+                                * Check for a checksum or name match
+                                */
+                               chksum_ok = (chksum == winChksum(dep->deName));
+                               if (!chksum_ok
+                                   && (!olddos || memcmp(dosfilename, dep->deName, 11))) {
+                                       chksum = -1;
+                                       continue;
+                               }
+                               DPRINTF(("%s(): match blkoff %d, diroff %d\n",
+                                   __func__, blkoff, diroff));
+                               /*
+                                * Remember where this directory
+                                * entry came from for whoever did
+                                * this lookup.
+                                */
+                               dp->de_fndoffset = diroff;
+                               dp->de_fndcnt = 0;
+
+                               return EEXIST;
+                       }
+               }       /* for (blkoff = 0; .... */
+               /*
+                * Release the buffer holding the directory cluster just
+                * searched.
+                */
+               brelse(bp, 0);
+       }       /* for (frcn = 0; ; frcn++) */
+
+notfound:
+       /*
+        * We hold no disk buffers at this point.
+        */
+
+       /*
+        * If we get here we didn't find the entry we were looking for. But
+        * that's ok if we are creating or renaming and are at the end of
+        * the pathname and the directory hasn't been removed.
+        */
+       DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n",
+           __func__, dp->de_refcnt, slotcount, slotoffset));
+       /*
+        * Fixup the slot description to point to the place where
+        * we might put the new DOS direntry (putting the Win95
+        * long name entries before that)
+        */
+       if (!slotcount) {
+               slotcount = 1;
+               slotoffset = diroff;
+       }
+       if (wincnt > slotcount) {
+               slotoffset += sizeof(struct direntry) * (wincnt - slotcount);
+       }
+
+       /*
+        * Return an indication of where the new directory
+        * entry should be put.
+        */
+       dp->de_fndoffset = slotoffset;
+       dp->de_fndcnt = wincnt - 1;
+
+       /*
+        * We return with the directory locked, so that
+        * the parameters we set up above will still be
+        * valid if we actually decide to do a direnter().
+        * We return ni_vp == NULL to indicate that the entry
+        * does not currently exist; we leave a pointer to
+        * the (locked) directory inode in ndp->ni_dvp.
+        *
+        * NB - if the directory is unlocked, then this
+        * information cannot be used.
+        */
+       return 0;
+}
+
+/*
+ * Create a regular file. On entry the directory to contain the file being
+ * created is locked.  We must release before we return.
+ */
+struct denode *
+msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node)
+{
+       struct componentname cn;
+       struct denode ndirent;
+       struct denode *dep;
+       int error;
+       struct stat *st = &node->inode->st;
+       struct msdosfsmount *pmp = pdep->de_pmp;
+
+       cn.cn_nameptr = node->name;
+       cn.cn_namelen = strlen(node->name);
+
+       DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__, node->name,
+           st->st_mode, (size_t)st->st_size));
+
+       /*
+        * If this is the root directory and there is no space left we
+        * can't do anything.  This is because the root directory can not
+        * change size.
+        */
+       if (pdep->de_StartCluster == MSDOSFSROOT
+           && pdep->de_fndoffset >= pdep->de_FileSize) {
+               error = ENOSPC;
+               goto bad;
+       }
+
+       /*
+        * Create a directory entry for the file, then call createde() to
+        * have it installed. NOTE: DOS files are always executable.  We
+        * use the absence of the owner write bit to make the file
+        * readonly.
+        */
+       memset(&ndirent, 0, sizeof(ndirent));
+       if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
+               goto bad;
+
+       ndirent.de_Attributes = (st->st_mode & S_IWUSR) ?
+                               ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
+       ndirent.de_StartCluster = 0;
+       ndirent.de_FileSize = 0;
+       ndirent.de_dev = pdep->de_dev;
+       ndirent.de_devvp = pdep->de_devvp;
+       ndirent.de_pmp = pdep->de_pmp;
+       ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
+       msdosfs_times(pmp, &ndirent, st);
+       if ((error = msdosfs_findslot(pdep, &cn)) != 0)
+               goto bad;
+       if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
+               goto bad;
+       if ((error = msdosfs_wfile(path, dep, node)) != 0)
+               goto bad;
+       return dep;
+
+bad:
+       errno = error;
+       return NULL;
+}
+static int
+msdosfs_updatede(struct denode *dep)
+{
+       struct buf *bp;
+       struct direntry *dirp;
+       int error;
+
+       dep->de_flag &= ~DE_MODIFIED;
+       error = readde(dep, &bp, &dirp);
+       if (error)
+               return error;
+       DE_EXTERNALIZE(dirp, dep);
+       error = bwrite(bp);
+       return error;
+}
+
+/*
+ * Write data to a file or directory.
+ */
+static int
+msdosfs_wfile(const char *path, struct denode *dep, fsnode *node)
+{
+       int error, fd;
+       size_t osize = dep->de_FileSize;
+       struct stat *st = &node->inode->st;
+       size_t nsize, offs;
+       struct msdosfsmount *pmp = dep->de_pmp;
+       struct buf *bp;
+       char *dat;
+       u_long cn = 0;
+
+       error = 0;      /* XXX: gcc/vax */
+       DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__,
+           dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster));
+       if (st->st_size == 0)
+               return 0;
+
+       /* Don't bother to try to write files larger than the fs limit */
+       if (st->st_size > MSDOSFS_FILESIZE_MAX) {
+               errno = EFBIG;
+               return -1;
+       }
+
+       nsize = st->st_size;
+       DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize));
+       if (nsize > osize) {
+               if ((error = deextend(dep, nsize, NULL)) != 0) {
+                       errno = error;
+                       return -1;
+               }
+               if ((error = msdosfs_updatede(dep)) != 0) {
+                       errno = error;
+                       return -1;
+               }
+       }
+
+       if ((fd = open(path, O_RDONLY)) == -1)
+               err(1, "open %s", path);
+
+       if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0))
+           == MAP_FAILED) {
+               DPRINTF(("%s: mmap %s %s", __func__, node->name,
+                   strerror(errno)));
+               close(fd);
+               goto out;
+       }
+       close(fd);
+
+       for (offs = 0; offs < nsize;) {
+               int blsize, cpsize;
+               daddr_t bn;
+               u_long on = offs & pmp->pm_crbomask;
+#ifdef HACK
+               cn = dep->de_StartCluster;
+               if (cn == MSDOSFSROOT) {
+                       DPRINTF(("%s: bad lbn %lu", __func__, cn));
+                       goto out;
+               }
+               bn = cntobn(pmp, cn);
+               blsize = pmp->pm_bpcluster;
+#else
+               if ((error = pcbmap(dep, cn++, &bn, NULL, &blsize)) != 0) {
+                       DPRINTF(("%s: pcbmap %lu", __func__, bn));
+                       goto out;
+               }
+#endif
+               DPRINTF(("%s(cn=%lu, bn=%llu/%llu, blsize=%d)\n", __func__,
+                   cn, (unsigned long long)bn,
+                   (unsigned long long)de_bn2kb(pmp, bn), blsize));
+               if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
+                   NULL, 0, &bp)) != 0) {
+                       DPRINTF(("bread %d\n", error));
+                       goto out;
+               } 
+               cpsize = MIN((nsize - offs), blsize - on);
+               memcpy((char *)bp->b_data + on, dat + offs, cpsize);
+               bwrite(bp);
+               offs += cpsize;
+       }
+
+       munmap(dat, nsize);
+       return 0;
+out:
+       munmap(dat, nsize);
+       return error;
+}
+
+
+static const struct {
+       struct direntry dot;
+       struct direntry dotdot;
+} dosdirtemplate = {
+       {       ".       ", "   ",                      /* the . entry */
+               ATTR_DIRECTORY,                         /* file attribute */
+               0,                                      /* reserved */
+               0, { 0, 0 }, { 0, 0 },                  /* create time & date */
+               { 0, 0 },                               /* access date */
+               { 0, 0 },                               /* high bits of start cluster */
+               { 210, 4 }, { 210, 4 },                 /* modify time & date */
+               { 0, 0 },                               /* startcluster */
+               { 0, 0, 0, 0 }                          /* filesize */
+       },
+       {       "..      ", "   ",                      /* the .. entry */
+               ATTR_DIRECTORY,                         /* file attribute */
+               0,                                      /* reserved */
+               0, { 0, 0 }, { 0, 0 },                  /* create time & date */
+               { 0, 0 },                               /* access date */
+               { 0, 0 },                               /* high bits of start cluster */
+               { 210, 4 }, { 210, 4 },                 /* modify time & date */
+               { 0, 0 },                               /* startcluster */
+               { 0, 0, 0, 0 }                          /* filesize */
+       }
+};
+
+struct denode *
+msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) {
+       struct denode ndirent;
+       struct denode *dep;
+       struct componentname cn;
+       struct stat *st = &node->inode->st;
+       struct msdosfsmount *pmp = pdep->de_pmp;
+       int error;
+       u_long newcluster, pcl, bn;
+       daddr_t lbn;
+       struct direntry *denp;
+       struct buf *bp;
+
+       cn.cn_nameptr = node->name;
+       cn.cn_namelen = strlen(node->name);
+       /*
+        * If this is the root directory and there is no space left we
+        * can't do anything.  This is because the root directory can not
+        * change size.
+        */
+       if (pdep->de_StartCluster == MSDOSFSROOT
+           && pdep->de_fndoffset >= pdep->de_FileSize) {
+               error = ENOSPC;
+               goto bad2;
+       }
+
+       /*
+        * Allocate a cluster to hold the about to be created directory.
+        */
+       error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
+       if (error)
+               goto bad2;
+
+       memset(&ndirent, 0, sizeof(ndirent));
+       ndirent.de_pmp = pmp;
+       ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
+       msdosfs_times(pmp, &ndirent, st);
+
+       /*
+        * Now fill the cluster with the "." and ".." entries. And write
+        * the cluster to disk.  This way it is there for the parent
+        * directory to be pointing at if there were a crash.
+        */
+       bn = cntobn(pmp, newcluster);
+       lbn = de_bn2kb(pmp, bn);
+       DPRINTF(("%s(newcluster %lu, bn=%lu, lbn=%lu)\n", __func__, newcluster,
+           bn, lbn));
+       /* always succeeds */
+       bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
+       memset(bp->b_data, 0, pmp->pm_bpcluster);
+       memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
+       denp = (struct direntry *)bp->b_data;
+       putushort(denp[0].deStartCluster, newcluster);
+       putushort(denp[0].deCDate, ndirent.de_CDate);
+       putushort(denp[0].deCTime, ndirent.de_CTime);
+       denp[0].deCHundredth = ndirent.de_CHun;
+       putushort(denp[0].deADate, ndirent.de_ADate);
+       putushort(denp[0].deMDate, ndirent.de_MDate);
+       putushort(denp[0].deMTime, ndirent.de_MTime);
+       pcl = pdep->de_StartCluster;
+       DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl,
+           pmp->pm_rootdirblk));
+       if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
+               pcl = 0;
+       putushort(denp[1].deStartCluster, pcl);
+       putushort(denp[1].deCDate, ndirent.de_CDate);
+       putushort(denp[1].deCTime, ndirent.de_CTime);
+       denp[1].deCHundredth = ndirent.de_CHun;
+       putushort(denp[1].deADate, ndirent.de_ADate);
+       putushort(denp[1].deMDate, ndirent.de_MDate);
+       putushort(denp[1].deMTime, ndirent.de_MTime);
+       if (FAT32(pmp)) {
+               putushort(denp[0].deHighClust, newcluster >> 16);
+               putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
+       } else {
+               putushort(denp[0].deHighClust, 0);
+               putushort(denp[1].deHighClust, 0);
+       }
+
+       if ((error = bwrite(bp)) != 0)
+               goto bad;
+
+       /*
+        * Now build up a directory entry pointing to the newly allocated
+        * cluster.  This will be written to an empty slot in the parent
+        * directory.
+        */
+       if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
+               goto bad;
+
+       ndirent.de_Attributes = ATTR_DIRECTORY;
+       ndirent.de_StartCluster = newcluster;
+       ndirent.de_FileSize = 0;
+       ndirent.de_dev = pdep->de_dev;
+       ndirent.de_devvp = pdep->de_devvp;
+       ndirent.de_pmp = pdep->de_pmp;
+       if ((error = msdosfs_findslot(pdep, &cn)) != 0)
+               goto bad;
+       if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
+               goto bad;
+       if ((error = msdosfs_updatede(dep)) != 0)
+               goto bad;
+       return dep;
+
+bad:
+       clusterfree(pmp, newcluster, NULL);
+bad2:
+       errno = error;
+       return NULL;
+}
diff --git a/usr.sbin/makefs/udf.c b/usr.sbin/makefs/udf.c
new file mode 100644 (file)
index 0000000..ad0136c
--- /dev/null
@@ -0,0 +1,1377 @@
+/* $NetBSD: udf.c,v 1.14 2013/10/19 17:16:37 christos Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: udf.c,v 1.14 2013/10/19 17:16:37 christos Exp $");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <util.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#define _EXPOSE_MMC
+#include <sys/cdio.h>
+#else
+#include "udf/cdio_mmc_structs.h"
+#endif
+
+#if !HAVE_NBTOOL_CONFIG_H
+#define HAVE_STRUCT_TM_TM_GMTOFF
+#endif
+
+#include "makefs.h"
+#include "udf_create.h"
+#include "udf_write.h"
+#include "newfs_udf.h"
+
+#undef APP_NAME
+#define APP_NAME "*NetBSD makefs"
+
+/*
+ * Note: due to the setup of the newfs code, the current state of the program
+ * and its options are helt in a few global variables. The FS specific parts
+ * are in a global `context' structure.
+ */
+
+/* global variables describing disc and format requests */
+int     fd;                            /* device: file descriptor */
+char   *dev;                           /* device: name            */
+struct mmc_discinfo mmc_discinfo;      /* device: disc info       */
+
+char   *format_str;                    /* format: string representation */
+int     format_flags;                  /* format: attribute flags       */
+int     media_accesstype;              /* derived from current mmc cap  */
+int     check_surface;                 /* for rewritables               */
+int     imagefile_secsize;             /* for files                     */
+
+int     display_progressbar;
+
+int     wrtrack_skew;
+float   meta_fract = (float) UDF_META_PERC / 100.0;
+
+int     mmc_profile;                   /* emulated profile */
+int     req_enable, req_disable;
+
+
+/* --------------------------------------------------------------------- */
+
+int
+udf_write_sector(void *sector, uint64_t location)
+{
+       uint64_t wpos;
+       ssize_t ret;
+
+       wpos = (uint64_t) location * context.sector_size;
+       ret = pwrite(fd, sector, context.sector_size, wpos);
+       if (ret == -1)
+               return errno;
+       if (ret < (int) context.sector_size)
+               return EIO;
+       return 0;
+}
+
+
+/* not implemented for files */
+int
+udf_surface_check(void)
+{
+       return 0;
+}
+
+
+/*
+ * mmc_discinfo and mmc_trackinfo readers modified from origional in udf main
+ * code in sys/fs/udf/
+ */
+
+#ifdef DEBUG
+static void
+udf_dump_discinfo(struct mmc_discinfo *di)
+{
+       char bits[128];
+
+       printf("Device/media info  :\n");
+       printf("\tMMC profile        0x%02x\n", di->mmc_profile);
+       printf("\tderived class      %d\n", di->mmc_class);
+       printf("\tsector size        %d\n", di->sector_size);
+       printf("\tdisc state         %d\n", di->disc_state);
+       printf("\tlast ses state     %d\n", di->last_session_state);
+       printf("\tbg format state    %d\n", di->bg_format_state);
+       printf("\tfrst track         %d\n", di->first_track);
+       printf("\tfst on last ses    %d\n", di->first_track_last_session);
+       printf("\tlst on last ses    %d\n", di->last_track_last_session);
+       printf("\tlink block penalty %d\n", di->link_block_penalty);
+       snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS,
+                       (uint64_t) di->disc_flags);
+       printf("\tdisc flags         %s\n", bits);
+       printf("\tdisc id            %x\n", di->disc_id);
+       printf("\tdisc barcode       %"PRIx64"\n", di->disc_barcode);
+
+       printf("\tnum sessions       %d\n", di->num_sessions);
+       printf("\tnum tracks         %d\n", di->num_tracks);
+
+       snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur);
+       printf("\tcapabilities cur   %s\n", bits);
+       snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap);
+       printf("\tcapabilities cap   %s\n", bits);
+       printf("\n");
+       printf("\tlast_possible_lba  %d\n", di->last_possible_lba);
+       printf("\n");
+}
+#else
+#define udf_dump_discinfo(a);
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static int
+udf_emulate_discinfo(fsinfo_t *fsopts, struct mmc_discinfo *di,
+               int mmc_emuprofile)
+{
+       off_t sectors;
+
+       memset(di, 0, sizeof(*di));
+
+       /* file support */
+       if ((mmc_emuprofile != 0x01) && (fsopts->sectorsize != 2048))
+               warnx("cd/dvd/bd sectorsize is not set to default 2048");
+
+       sectors = fsopts->size / fsopts->sectorsize;
+
+       /* commons */
+       di->mmc_profile         = mmc_emuprofile;
+       di->disc_state          = MMC_STATE_CLOSED;
+       di->last_session_state  = MMC_STATE_CLOSED;
+       di->bg_format_state     = MMC_BGFSTATE_COMPLETED;
+       di->link_block_penalty  = 0;
+
+       di->disc_flags = MMC_DFLAGS_UNRESTRICTED;
+
+       di->last_possible_lba = sectors - 1;
+       di->sector_size       = fsopts->sectorsize;
+
+       di->num_sessions = 1;
+       di->num_tracks   = 1;
+
+       di->first_track  = 1;
+       di->first_track_last_session = di->last_track_last_session = 1;
+
+       di->mmc_cur = MMC_CAP_RECORDABLE | MMC_CAP_ZEROLINKBLK;
+       switch (mmc_emuprofile) {
+       case 0x00:      /* unknown, treat as CDROM */
+       case 0x08:      /* CDROM */
+       case 0x10:      /* DVDROM */
+       case 0x40:      /* BDROM */
+               req_enable |= FORMAT_READONLY;
+               /* FALLTROUGH */
+       case 0x01:      /* disc */
+               /* set up a disc info profile for partitions/files */
+               di->mmc_class   = MMC_CLASS_DISC;
+               di->mmc_cur    |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE;
+               break;
+       case 0x09:      /* CD-R */
+               di->mmc_class   = MMC_CLASS_CD;
+               di->mmc_cur    |= MMC_CAP_SEQUENTIAL;
+               di->disc_state  = MMC_STATE_EMPTY;
+               break;
+       case 0x0a:      /* CD-RW + CD-MRW (regretably) */
+               di->mmc_class   = MMC_CLASS_CD;
+               di->mmc_cur    |= MMC_CAP_REWRITABLE;
+               break;
+       case 0x13:      /* DVD-RW */
+               di->mmc_class   = MMC_CLASS_DVD;
+               di->mmc_cur    |= MMC_CAP_REWRITABLE;
+               break;
+       case 0x11:      /* DVD-R */
+       case 0x14:      /* DVD-RW sequential */
+       case 0x1b:      /* DVD+R */
+       case 0x2b:      /* DVD+R DL */
+       case 0x51:      /* HD DVD-R */
+               di->mmc_class   = MMC_CLASS_DVD;
+               di->mmc_cur    |= MMC_CAP_SEQUENTIAL;
+               di->disc_state  = MMC_STATE_EMPTY;
+               break;
+       case 0x41:      /* BD-R */
+               di->mmc_class   = MMC_CLASS_BD;
+               di->mmc_cur    |= MMC_CAP_SEQUENTIAL | MMC_CAP_HW_DEFECTFREE;
+               di->disc_state  = MMC_STATE_EMPTY;
+               break;
+       case 0x43:      /* BD-RE */
+               di->mmc_class   = MMC_CLASS_BD;
+               di->mmc_cur    |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE;
+               break;
+       default:
+               err(EINVAL, "makefs_udf: unknown or unimplemented device type");
+               return EINVAL;
+       }
+       di->mmc_cap    = di->mmc_cur;
+
+       udf_dump_discinfo(di);
+       return 0;
+}
+
+
+int
+udf_update_trackinfo(struct mmc_discinfo *di, struct mmc_trackinfo *ti)
+{
+       /* discs partition support */
+       if (ti->tracknr != 1)
+               return EIO;
+
+       /* create fake ti */
+       ti->sessionnr  = 1;
+
+       ti->track_mode = 0;     /* XXX */
+       ti->data_mode  = 0;     /* XXX */
+       ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID;
+
+       ti->track_start    = 0;
+       ti->packet_size    = 32;        /* take sensible default */
+
+       ti->track_size    = di->last_possible_lba;
+       ti->next_writable = di->last_possible_lba;
+       ti->last_recorded = ti->next_writable;
+       ti->free_blocks   = 0;
+
+       return 0;
+}
+
+#define OPT_STR(letter, name, desc)  \
+       { letter, name, NULL, OPT_STRBUF, 0, 0, desc }
+
+#define OPT_NUM(letter, name, field, min, max, desc) \
+       { letter, name, &context.field, \
+         sizeof(context.field) == 8 ? OPT_INT64 : \
+         (sizeof(context.field) == 4 ? OPT_INT32 : \
+         (sizeof(context.field) == 2 ? OPT_INT16 : OPT_INT8)), \
+         min, max, desc }
+
+#define OPT_BOOL(letter, name, field, desc) \
+       OPT_NUM(letter, name, field, 0, 1, desc)
+
+void
+udf_prep_opts(fsinfo_t *fsopts)
+{
+       struct tm *tm;
+       time_t now;
+
+       const option_t udf_options[] = {
+               OPT_STR('T', "disctype", "disc type (cdrom,dvdrom,bdrom,"
+                       "dvdram,bdre,disk,cdr,dvdr,bdr,cdrw,dvdrw)"),
+               OPT_STR('L', "loglabel", "\"logical volume name\""),
+               OPT_STR('P', "discid",   "\"[volset name ':']"
+                       "physical volume name\""),
+               OPT_NUM('t', "tz", gmtoff, -24, 24, "timezone"),
+               OPT_STR('v', "minver", "minimum UDF version in either "
+                       "``0x201'' or ``2.01'' format"),
+#if notyet
+               OPT_STR('V', "maxver", "maximum UDF version in either "
+                       "``0x201'' or ``2.01'' format"),
+#endif
+               { .name = NULL }
+       };
+
+       /* initialise */
+       format_str    = strdup("");
+       req_enable    = req_disable = 0;
+       format_flags  = FORMAT_INVALID;
+       fsopts->sectorsize = 512;       /* minimum allowed sector size */
+
+       srandom((unsigned long) time(NULL));
+
+       mmc_profile = 0x01;             /* 'disc'/file */
+
+       udf_init_create_context();
+       context.app_name         = "*NetBSD makefs";
+       context.app_version_main = APP_VERSION_MAIN;
+       context.app_version_sub  = APP_VERSION_SUB;
+       context.impl_name        = IMPL_NAME;
+
+       /* minimum and maximum UDF versions we advise */
+       context.min_udf = 0x102;
+       context.max_udf = 0x201;        /* 0x250 and 0x260 are not ready */
+
+       /* use user's time zone as default */
+       (void)time(&now);
+       tm = localtime(&now);
+#ifdef HAVE_STRUCT_TM_TM_GMTOFF
+       context.gmtoff = tm->tm_gmtoff;
+#else
+       context.gmtoff = 0;
+#endif
+
+       /* return info */
+       fsopts->fs_specific = NULL;
+       fsopts->fs_options = copy_opts(udf_options);
+}
+
+
+void
+udf_cleanup_opts(fsinfo_t *fsopts)
+{
+       free(fsopts->fs_options);
+}
+
+
+/* ----- included from newfs_udf.c ------ */
+/* ----- */
+
+
+#define CDRSIZE    ((uint64_t)   700*1024*1024)        /* small approx */
+#define CDRWSIZE   ((uint64_t)   576*1024*1024)        /* small approx */
+#define DVDRSIZE   ((uint64_t)  4488*1024*1024)        /* small approx */
+#define DVDRAMSIZE ((uint64_t)  4330*1024*1024)        /* small approx with spare */
+#define DVDRWSIZE  ((uint64_t)  4482*1024*1024)        /* small approx */
+#define BDRSIZE    ((uint64_t) 23866*1024*1024)        /* small approx */
+#define BDRESIZE   ((uint64_t) 23098*1024*1024)        /* small approx */
+int
+udf_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+       option_t *udf_options = fsopts->fs_options;
+       uint64_t stdsize;
+       uint32_t set_sectorsize;
+       char buffer[1024], *buf, *colon;
+       int i;
+
+       assert(option != NULL);
+
+       if (debug & DEBUG_FS_PARSE_OPTS)
+               printf("udf_parse_opts: got `%s'\n", option);
+
+       i = set_option(udf_options, option, buffer, sizeof(buffer));
+       if (i == -1)
+               return 0;
+
+       if (udf_options[i].name == NULL)
+               abort();
+
+       set_sectorsize = 0;
+       stdsize = 0;
+
+       buf = buffer;
+       switch (udf_options[i].letter) {
+       case 'T':
+               if (strcmp(buf, "cdrom") == 0) {
+                       mmc_profile = 0x00;
+               } else if (strcmp(buf, "dvdrom") == 0) {
+                       mmc_profile = 0x10;
+               } else if (strcmp(buf, "bdrom") == 0) {
+                       mmc_profile = 0x40;
+               } else if (strcmp(buf, "dvdram") == 0) {
+                       mmc_profile = 0x12;
+                       stdsize = DVDRAMSIZE;
+               } else if (strcmp(buf, "bdre") == 0) {
+                       mmc_profile = 0x43;
+                       stdsize = BDRESIZE;
+               } else if (strcmp(buf, "disk") == 0) {
+                       mmc_profile = 0x01;
+               } else if (strcmp(buf, "cdr") == 0) {
+                       mmc_profile = 0x09;
+                       stdsize = CDRSIZE;
+               } else if (strcmp(buf, "dvdr") == 0) {
+                       mmc_profile = 0x1b;
+                       stdsize = DVDRSIZE;
+               } else if (strcmp(buf, "bdr") == 0) {
+                       mmc_profile = 0x41;
+                       stdsize = BDRSIZE;
+               } else if (strcmp(buf, "cdrw") == 0) {
+                       mmc_profile = 0x0a;
+                       stdsize = CDRWSIZE;
+               } else if (strcmp(buf, "dvdrw") == 0) {
+                       mmc_profile = 0x13;
+                       stdsize = DVDRWSIZE;
+               } else {
+                       errx(EINVAL, "Unknown or unimplemented disc format");
+                       return 0;
+               }
+               if (mmc_profile != 0x01)
+                       set_sectorsize = 2048;
+               break;
+       case 'L':
+               if (context.logvol_name) free(context.logvol_name);
+               context.logvol_name = strdup(buf);
+               break;
+       case 'P':
+               if ((colon = strstr(buf, ":"))) {
+                       if (context.volset_name)
+                               free(context.volset_name);
+                       *colon = 0;
+                       context.volset_name = strdup(buf);
+                       buf = colon+1;
+               }
+               if (context.primary_name)
+                       free(context.primary_name);
+               if ((strstr(buf, ":"))) {
+                       errx(EINVAL, "primary name can't have ':' in its name");
+                       return 0;
+               }
+               context.primary_name = strdup(buf);
+               break;
+       case 'v':
+               context.min_udf = a_udf_version(buf, "min_udf");
+               if (context.min_udf > 0x201) {
+                       errx(EINVAL, "maximum supported version is UDF 2.01");
+                       return 0;
+               }
+               if (context.min_udf > context.max_udf)
+                       context.max_udf = context.min_udf;
+               break;
+       }
+       if (set_sectorsize)
+               fsopts->sectorsize = set_sectorsize;
+       if (stdsize)
+               fsopts->size = stdsize;
+       return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+struct udf_stats {
+       uint32_t nfiles;
+       uint32_t ndirs;
+       uint32_t ndescr;
+       uint32_t nmetadatablocks;
+       uint32_t ndatablocks;
+};
+
+
+/* node reference administration */
+static void
+udf_inc_link(union dscrptr *dscr)
+{
+       struct file_entry *fe;
+       struct extfile_entry *efe;
+
+       if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
+               fe        = &dscr->fe;
+               fe->link_cnt = udf_rw16(udf_rw16(fe->link_cnt) + 1);
+       } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
+               efe       = &dscr->efe;
+               efe->link_cnt = udf_rw16(udf_rw16(efe->link_cnt) + 1);
+       } else {
+               errx(1, "Bad tag passed to udf_inc_link");
+       }
+}
+
+
+static void
+udf_set_link_cnt(union dscrptr *dscr, int num)
+{
+       struct file_entry *fe;
+       struct extfile_entry *efe;
+
+       if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
+               fe        = &dscr->fe;
+               fe->link_cnt = udf_rw16(num);
+       } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
+               efe       = &dscr->efe;
+               efe->link_cnt = udf_rw16(num);
+       } else {
+               errx(1, "Bad tag passed to udf_set_link_cnt");
+       }
+}
+
+
+static uint32_t
+udf_datablocks(off_t sz)
+{
+       /* predictor if it can be written inside the node */
+       if (sz < context.sector_size - UDF_EXTFENTRY_SIZE - 16)
+               return 0;
+
+       return UDF_ROUNDUP(sz, context.sector_size) / context.sector_size;
+}
+
+
+static void
+udf_prepare_fids(struct long_ad *dir_icb, struct long_ad *dirdata_icb,
+               uint8_t *dirdata, uint32_t dirdata_size)
+{
+       struct fileid_desc *fid;
+       struct long_ad     *icb;
+       uint32_t fidsize, offset;
+       uint32_t location;
+
+       if (udf_datablocks(dirdata_size) == 0) {
+               /* going internal */
+               icb = dir_icb;
+       } else {
+               /* external blocks to write to */
+               icb = dirdata_icb;
+       }
+
+       for (offset = 0; offset < dirdata_size; offset += fidsize) {
+               /* for each FID: */
+               fid = (struct fileid_desc *) (dirdata + offset);
+               assert(udf_rw16(fid->tag.id) == TAGID_FID);
+
+               location  = udf_rw32(icb->loc.lb_num);
+               location += offset / context.sector_size;
+
+               fid->tag.tag_loc = udf_rw32(location);
+               udf_validate_tag_and_crc_sums((union dscrptr *) fid);
+
+               fidsize = udf_fidsize(fid);
+       }
+}
+
+static int
+udf_file_inject_blob(union dscrptr *dscr,  uint8_t *blob, off_t size)
+{
+       struct icb_tag *icb;
+       struct file_entry *fe;
+       struct extfile_entry *efe;
+       uint64_t inf_len, obj_size;
+       uint32_t l_ea, l_ad;
+       uint32_t free_space, desc_size;
+       uint16_t crclen;
+       uint8_t *data, *pos;
+
+       fe = NULL;
+       efe = NULL;
+       if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
+               fe        = &dscr->fe;
+               data      = fe->data;
+               l_ea      = udf_rw32(fe->l_ea);
+               l_ad      = udf_rw32(fe->l_ad);
+               icb       = &fe->icbtag;
+               inf_len   = udf_rw64(fe->inf_len);
+               obj_size  = 0;
+               desc_size = sizeof(struct file_entry);
+       } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
+               efe       = &dscr->efe;
+               data      = efe->data;
+               l_ea      = udf_rw32(efe->l_ea);
+               l_ad      = udf_rw32(efe->l_ad);
+               icb       = &efe->icbtag;
+               inf_len   = udf_rw64(efe->inf_len);
+               obj_size  = udf_rw64(efe->obj_size);
+               desc_size = sizeof(struct extfile_entry);
+       } else {
+               errx(1, "Bad tag passed to udf_file_inject_blob");
+       }
+       crclen = udf_rw16(dscr->tag.desc_crc_len);
+
+       /* calculate free space */
+       free_space = context.sector_size - (l_ea + l_ad) - desc_size;
+       if (udf_datablocks(size)) {
+               assert(free_space < size);
+               return 1;
+       }
+
+       /* going internal */
+       assert(l_ad == 0);
+       assert((udf_rw16(icb->flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) ==
+                       UDF_ICB_INTERN_ALLOC);
+
+       // assert(free_space >= size);
+       pos = data + l_ea + l_ad;
+       memcpy(pos, blob, size);
+       l_ad   += size;
+       crclen += size;
+
+       inf_len  += size;
+       obj_size += size;
+
+       if (fe) {
+               fe->l_ad = udf_rw32(l_ad);
+               fe->inf_len = udf_rw64(inf_len);
+       } else if (efe) {
+               efe->l_ad = udf_rw32(l_ad);
+               efe->inf_len  = udf_rw64(inf_len);
+               efe->obj_size = udf_rw64(inf_len);
+       }
+
+       /* make sure the header sums stays correct */
+       dscr->tag.desc_crc_len = udf_rw16(crclen);
+       udf_validate_tag_and_crc_sums(dscr);
+
+       return 0;
+}
+
+
+/* XXX no sparse file support */
+static void
+udf_append_file_mapping(union dscrptr *dscr, struct long_ad *piece)
+{
+       struct icb_tag *icb;
+       struct file_entry *fe;
+       struct extfile_entry *efe;
+       struct long_ad *last_long, last_piece;
+       struct short_ad *last_short, new_short;
+       uint64_t inf_len, obj_size, logblks_rec;
+       uint32_t l_ea, l_ad, size;
+       uint32_t last_lb_num, piece_lb_num;
+       uint64_t last_len, piece_len, last_flags;
+       uint64_t rest_len, merge_len, last_end;
+       uint16_t last_part_num, piece_part_num;
+       uint16_t crclen, cur_alloc;
+       uint8_t *data, *pos;
+       const int short_len = sizeof(struct short_ad);
+       const int long_len  = sizeof(struct long_ad);
+       const int sector_size = context.sector_size;
+       const int use_shorts = (context.data_part == context.metadata_part);
+       uint64_t max_len = UDF_ROUNDDOWN(UDF_EXT_MAXLEN, sector_size);
+
+       fe  = NULL;
+       efe = NULL;
+       if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
+               fe          = &dscr->fe;
+               data        = fe->data;
+               l_ea        = fe->l_ea;
+               l_ad        = udf_rw32(fe->l_ad);
+               icb         = &fe->icbtag;
+               inf_len     = udf_rw64(fe->inf_len);
+               logblks_rec = udf_rw64(fe->logblks_rec);
+               obj_size = 0;
+       } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
+               efe         = &dscr->efe;
+               data        = efe->data;
+               l_ea        = efe->l_ea;
+               l_ad        = udf_rw32(efe->l_ad);
+               icb         = &efe->icbtag;
+               inf_len     = udf_rw64(efe->inf_len);
+               obj_size    = udf_rw64(efe->obj_size);
+               logblks_rec = udf_rw64(efe->logblks_rec);
+       } else {
+               errx(1, "Bad tag passed to udf_file_append_blob");
+       }
+       crclen = udf_rw16(dscr->tag.desc_crc_len);
+
+       pos = data + l_ea;
+       cur_alloc = udf_rw16(icb->flags);
+       size = UDF_EXT_LEN(udf_rw32(piece->len));
+
+       /* extract last entry as a long_ad */
+       memset(&last_piece, 0, sizeof(last_piece));
+       last_len      = 0;
+       last_lb_num   = 0;
+       last_part_num = 0;
+       last_flags    = 0;
+       last_short    = NULL;
+       last_long     = NULL;
+       if (l_ad != 0) {
+               if (use_shorts) {
+                       assert(cur_alloc == UDF_ICB_SHORT_ALLOC);
+                       pos += l_ad - short_len;
+                       last_short   = (struct short_ad *) pos;
+                       last_lb_num  = udf_rw32(last_short->lb_num);
+                       last_part_num = udf_rw16(piece->loc.part_num);
+                       last_len     = UDF_EXT_LEN(udf_rw32(last_short->len));
+                       last_flags   = UDF_EXT_FLAGS(udf_rw32(last_short->len));
+               } else {
+                       assert(cur_alloc == UDF_ICB_LONG_ALLOC);
+                       pos += l_ad - long_len;
+                       last_long    = (struct long_ad *) pos;
+                       last_lb_num  = udf_rw32(last_long->loc.lb_num);
+                       last_part_num = udf_rw16(last_long->loc.part_num);
+                       last_len     = UDF_EXT_LEN(udf_rw32(last_long->len));
+                       last_flags   = UDF_EXT_FLAGS(udf_rw32(last_long->len));
+               }
+       }
+
+       piece_len      = UDF_EXT_LEN(udf_rw32(piece->len));
+       piece_lb_num   = udf_rw32(piece->loc.lb_num);
+       piece_part_num = udf_rw16(piece->loc.part_num);
+
+       /* try merging */
+       rest_len  = max_len - last_len;
+
+       merge_len = MIN(piece_len, rest_len);
+       last_end  = last_lb_num + (last_len / sector_size);
+
+       if ((piece_lb_num == last_end) && (last_part_num == piece_part_num)) {
+               /* we can merge */
+               last_len  += merge_len;
+               piece_len -= merge_len;
+
+               /* write back merge result */
+               if (use_shorts) {
+                       last_short->len = udf_rw32(last_len | last_flags);
+               } else {
+                       last_long->len  = udf_rw32(last_len | last_flags);
+               }
+               piece_lb_num += merge_len / sector_size;
+       }
+
+       if (piece_len) {
+               /* append new entry */
+               pos = data + l_ea + l_ad;
+               if (use_shorts) {
+                       icb->flags = udf_rw16(UDF_ICB_SHORT_ALLOC);
+                       memset(&new_short, 0, short_len);
+                       new_short.len    = udf_rw32(piece_len);
+                       new_short.lb_num = udf_rw32(piece_lb_num);
+                       memcpy(pos, &new_short, short_len);
+                       l_ad += short_len;
+                       crclen += short_len;
+               } else {
+                       icb->flags = udf_rw16(UDF_ICB_LONG_ALLOC);
+                       piece->len        = udf_rw32(piece_len);
+                       piece->loc.lb_num = udf_rw32(piece_lb_num);
+                       memcpy(pos, piece, long_len);
+                       l_ad += long_len;
+                       crclen += long_len;
+               }
+       }
+       piece->len = udf_rw32(0);
+
+       inf_len  += size;
+       obj_size += size;
+       logblks_rec += UDF_ROUNDUP(size, sector_size) / sector_size;
+
+       dscr->tag.desc_crc_len = udf_rw16(crclen);
+       if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
+               fe->l_ad = udf_rw32(l_ad);
+               fe->inf_len = udf_rw64(inf_len);
+               fe->logblks_rec = udf_rw64(logblks_rec);
+       } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
+               efe->l_ad = udf_rw32(l_ad);
+               efe->inf_len  = udf_rw64(inf_len);
+               efe->obj_size = udf_rw64(inf_len);
+               efe->logblks_rec = udf_rw64(logblks_rec);
+       }
+}
+
+
+static int
+udf_append_file_contents(union dscrptr *dscr, struct long_ad *data_icb,
+               uint8_t *fdata, off_t flen)
+{
+       struct long_ad icb;
+       uint32_t location;
+       uint64_t phys;
+       uint16_t vpart;
+       uint8_t *bpos;
+       int cnt, sects;
+       int error;
+
+       if (udf_file_inject_blob(dscr, fdata, flen) == 0)
+               return 0;
+
+       /* has to be appended in mappings */
+       icb = *data_icb;
+       icb.len = udf_rw32(flen);
+       while (udf_rw32(icb.len) > 0)
+               udf_append_file_mapping(dscr, &icb);
+       udf_validate_tag_and_crc_sums(dscr);
+
+       /* write out data piece */
+       vpart    = udf_rw16(data_icb->loc.part_num);
+       location = udf_rw32(data_icb->loc.lb_num);
+       sects    = udf_datablocks(flen);
+       for (cnt = 0; cnt < sects; cnt++) {
+               bpos = fdata + cnt*context.sector_size;
+               phys = context.vtop_offset[vpart] + location + cnt;
+               error = udf_write_sector(bpos, phys);
+               if (error)
+                       return error;
+       }
+       return 0;
+}
+
+
+static int
+udf_create_new_file(struct stat *st, union dscrptr **dscr,
+       int filetype, struct long_ad *icb)
+{
+       struct file_entry *fe;
+       struct extfile_entry *efe;
+       int error;
+
+       fe = NULL;
+       efe = NULL;
+       if (context.dscrver == 2) {
+               error = udf_create_new_fe(&fe, filetype, st);
+               if (error)
+                       errx(error, "can't create fe");
+               *dscr = (union dscrptr *) fe;
+               icb->longad_uniqueid = fe->unique_id;
+       } else {
+               error = udf_create_new_efe(&efe, filetype, st);
+               if (error)
+                       errx(error, "can't create fe");
+               *dscr = (union dscrptr *) efe;
+               icb->longad_uniqueid = efe->unique_id;
+       }
+
+       return 0;
+}
+
+
+static void
+udf_estimate_walk(fsinfo_t *fsopts,
+               fsnode *root, char *dir, struct udf_stats *stats)
+{
+       struct fileid_desc *fid;
+       struct long_ad dummy_ref;
+       fsnode *cur;
+       fsinode *fnode;
+       size_t pathlen = strlen(dir);
+       char *mydir = dir + pathlen;
+       off_t sz;
+       uint32_t nblk, ddoff;
+       uint32_t softlink_len;
+       uint8_t *softlink_buf;
+       int nentries;
+       int error;
+
+       stats->ndirs++;
+
+       /*
+        * Count number of directory entries and count directory size; needed
+        * for the reservation of enough space for the directory. Pity we
+        * don't keep the FIDs we created. If it turns out to be a issue we
+        * can cache it later.
+        */
+       fid = (struct fileid_desc *) malloc(context.sector_size);
+       assert(fid);
+
+       ddoff = 40;     /* '..' entry */
+       for (cur = root, nentries = 0; cur != NULL; cur = cur->next) {
+               switch (cur->type & S_IFMT) {
+               default:
+                       /* what kind of nodes? */
+                       break;
+               case S_IFCHR:
+               case S_IFBLK:
+                       /* not supported yet */
+                       continue;
+               case S_IFDIR:
+                       if (strcmp(cur->name, ".") == 0)
+                               continue;
+               case S_IFLNK:
+               case S_IFREG:
+                       /* create dummy FID to see how long name will become */
+                       udf_create_fid(ddoff, fid, cur->name, 0, &dummy_ref);
+
+                       nentries++;
+                       ddoff += udf_fidsize(fid);
+               }
+       }
+       sz = ddoff;
+
+       root->inode->st.st_size = sz;   /* max now */
+       root->inode->flags |= FI_SIZED;
+
+       nblk = udf_datablocks(sz);
+       stats->nmetadatablocks += nblk;
+
+       /* for each entry in the directory, there needs to be a (E)FE */
+       stats->nmetadatablocks += nentries + 1;
+
+       /* recurse */
+       for (cur = root; cur != NULL; cur = cur->next) {
+               switch (cur->type & S_IFMT) {
+               default:
+                       /* what kind of nodes? */
+                       break;
+               case S_IFCHR:
+               case S_IFBLK:
+                       /* not supported yet */
+                       // stats->nfiles++;
+                       break;
+               case S_IFDIR:
+                       if (strcmp(cur->name, ".") == 0)
+                               continue;
+                       /* empty dir? */
+                       if (!cur->child)
+                               break;
+                       mydir[0] = '/';
+                       strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen);
+                       udf_estimate_walk(fsopts, cur->child, dir, stats);
+                       mydir[0] = '\0';
+                       break;
+               case S_IFREG:
+                       fnode = cur->inode;
+                       /* don't double-count hard-links */
+                       if (!(fnode->flags & FI_SIZED)) {
+                               sz = fnode->st.st_size;
+                               nblk = udf_datablocks(sz);
+                               stats->ndatablocks += nblk;
+                               /* ... */
+                               fnode->flags |= FI_SIZED;
+                       }
+                       stats->nfiles++;
+                       break;
+               case S_IFLNK:
+                       /* softlink */
+                       fnode = cur->inode;
+                       /* don't double-count hard-links */
+                       if (!(fnode->flags & FI_SIZED)) {
+                               error = udf_encode_symlink(&softlink_buf,
+                                               &softlink_len, cur->symlink);
+                               if (error) {
+                                       printf("SOFTLINK error %d\n", error);
+                                       break;
+                               }
+                               nblk = udf_datablocks(softlink_len);
+                               stats->ndatablocks += nblk;
+                               fnode->flags |= FI_SIZED;
+
+                               free(softlink_buf);
+                       }
+                       stats->nfiles++;
+                       break;
+               }
+       }
+}
+
+
+#define UDF_MAX_CHUNK_SIZE (4*1024*1024)
+static int
+udf_copy_file(struct stat *st, char *path, fsnode *cur, struct fileid_desc *fid,
+       struct long_ad *icb)
+{
+       union dscrptr *dscr;
+       struct long_ad data_icb;
+       fsinode *fnode;
+       off_t sz, chunk, rd;
+       uint8_t *data;
+       int nblk;
+       int i, f;
+       int error;
+
+       fnode = cur->inode;
+
+       f = open(path, O_RDONLY);
+       if (f < 0) {
+               warn("Can't open file %s for reading", cur->name);
+               return errno;
+       }
+
+       /* claim disc space for the (e)fe descriptor for this file */
+       udf_metadata_alloc(1, icb);
+       udf_create_new_file(st, &dscr, UDF_ICB_FILETYPE_RANDOMACCESS, icb);
+
+       sz = fnode->st.st_size;
+
+       chunk = MIN(sz, UDF_MAX_CHUNK_SIZE);
+       data = malloc(MAX(chunk, context.sector_size));
+       assert(data);
+
+       printf("  ");
+       i = 0;
+       error = 0;
+       while (chunk) {
+               rd = read(f, data, chunk);
+               if (rd != chunk) {
+                       warn("Short read of file %s\n", cur->name);
+                       error = errno;
+                       break;
+               }
+               printf("\b%c", "\\|/-"[i++ % 4]); fflush(stdout);fflush(stderr);
+
+               nblk = udf_datablocks(chunk);
+               if (nblk > 0)
+                       udf_data_alloc(nblk, &data_icb);
+               udf_append_file_contents(dscr, &data_icb, data, chunk);
+
+               sz -= chunk;
+               chunk = MIN(sz, UDF_MAX_CHUNK_SIZE);
+       }
+       printf("\b \n");
+       close(f);
+       free(data);
+
+       /* write out dscr (e)fe */
+       udf_set_link_cnt(dscr, fnode->nlink);
+       udf_write_dscr_virt(dscr, udf_rw32(icb->loc.lb_num),
+               udf_rw16(icb->loc.part_num), 1);
+       free(dscr);
+
+       /* remember our location for hardlinks */
+       cur->inode->fsuse = malloc(sizeof(struct long_ad));
+       memcpy(cur->inode->fsuse, icb, sizeof(struct long_ad));
+
+       return error;
+}
+
+
+static int
+udf_populate_walk(fsinfo_t *fsopts, fsnode *root, char *dir,
+               struct long_ad *parent_icb, struct long_ad *dir_icb)
+{
+       union dscrptr *dir_dscr, *dscr;
+       struct fileid_desc *fid;
+       struct long_ad icb, data_icb, dirdata_icb;
+       fsnode *cur;
+       fsinode *fnode;
+       size_t pathlen = strlen(dir);
+       size_t dirlen;
+       char *mydir = dir + pathlen;
+       uint32_t nblk, ddoff;
+       uint32_t softlink_len;
+       uint8_t *softlink_buf;
+       uint8_t *dirdata;
+       int error, ret, retval;
+
+       /* claim disc space for the (e)fe descriptor for this dir */
+       udf_metadata_alloc(1, dir_icb);
+
+       /* create new e(fe) */
+       udf_create_new_file(&root->inode->st, &dir_dscr,
+               UDF_ICB_FILETYPE_DIRECTORY, dir_icb);
+
+       /* claim space for the directory contents */
+       dirlen = root->inode->st.st_size;
+       nblk = udf_datablocks(dirlen);
+       if (nblk > 0) {
+               /* claim disc space for the dir contents */
+               udf_data_alloc(nblk, &dirdata_icb);
+       }
+
+       /* allocate memory for the directory contents */
+       nblk++;
+       dirdata = malloc(nblk * context.sector_size);
+       assert(dirdata);
+       memset(dirdata, 0, nblk * context.sector_size);
+
+       /* create and append '..' */
+       fid = (struct fileid_desc *) dirdata;
+       ddoff = udf_create_parentfid(fid, parent_icb);
+
+       /* for '..' */
+       udf_inc_link(dir_dscr);
+
+       /* recurse */
+       retval = 0;
+       for (cur = root; cur != NULL; cur = cur->next) {
+               mydir[0] = '/';
+               strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen);
+
+               fid = (struct fileid_desc *) (dirdata + ddoff);
+               switch (cur->type & S_IFMT) {
+               default:
+                       /* what kind of nodes? */
+                       retval = 2;
+                       break;
+               case S_IFCHR:
+               case S_IFBLK:
+                       /* not supported */
+                       retval = 2;
+                       warnx("device node %s not supported", dir);
+                       break;
+               case S_IFDIR:
+                       /* not an empty dir? */
+                       if (strcmp(cur->name, ".") == 0)
+                               break;
+                       assert(cur->child);
+                       if (cur->child) {
+                               ret = udf_populate_walk(fsopts, cur->child,
+                                       dir, dir_icb, &icb);
+                               if (ret)
+                                       retval = 2;
+                       }
+                       udf_create_fid(ddoff, fid, cur->name,
+                                       UDF_FILE_CHAR_DIR, &icb);
+                       udf_inc_link(dir_dscr);
+                       ddoff += udf_fidsize(fid);
+                       break;
+               case S_IFREG:
+                       fnode = cur->inode;
+                       /* don't re-copy hard-links */
+                       if (!(fnode->flags & FI_WRITTEN)) {
+                               printf("%s", dir);
+                               error = udf_copy_file(&fnode->st, dir, cur,
+                                       fid, &icb);
+                               if (!error) {
+                                       fnode->flags |= FI_WRITTEN;
+                                       udf_create_fid(ddoff, fid, cur->name,
+                                               0, &icb);
+                                       ddoff += udf_fidsize(fid);
+                               } else {
+                                       retval = 2;
+                               }
+                       } else {
+                               /* hardlink! */
+                               printf("%s (hardlink)\n", dir);
+                               udf_create_fid(ddoff, fid, cur->name,
+                                       0, (struct long_ad *) (fnode->fsuse));
+                               ddoff += udf_fidsize(fid);
+                       }
+                       fnode->nlink--;
+                       if (fnode->nlink == 0)
+                               free(fnode->fsuse);
+                       break;
+               case S_IFLNK:
+                       /* softlink */
+                       fnode = cur->inode;
+                       printf("%s -> %s\n", dir, cur->symlink);
+                       error = udf_encode_symlink(&softlink_buf,
+                                       &softlink_len, cur->symlink);
+                       if (error) {
+                               printf("SOFTLINK error %d\n", error);
+                               retval = 2;
+                               break;
+                       }
+
+                       udf_metadata_alloc(1, &icb);
+                       udf_create_new_file(&fnode->st, &dscr,
+                               UDF_ICB_FILETYPE_SYMLINK, &icb);
+
+                       nblk = udf_datablocks(softlink_len);
+                       if (nblk > 0)
+                               udf_data_alloc(nblk, &data_icb);
+                       udf_append_file_contents(dscr, &data_icb,
+                                       softlink_buf, softlink_len);
+
+                       /* write out dscr (e)fe */
+                       udf_inc_link(dscr);
+                       udf_write_dscr_virt(dscr, udf_rw32(icb.loc.lb_num),
+                               udf_rw16(icb.loc.part_num), 1);
+
+                       free(dscr);
+                       free(softlink_buf);
+
+                       udf_create_fid(ddoff, fid, cur->name, 0, &icb);
+                       ddoff += udf_fidsize(fid);
+                       break;
+               }
+               mydir[0] = '\0';
+       }
+
+       /* writeout directory contents */
+       dirlen = ddoff; /* XXX might bite back */
+
+       udf_prepare_fids(dir_icb, &dirdata_icb, dirdata, dirlen);
+       udf_append_file_contents(dir_dscr, &dirdata_icb, dirdata, dirlen);
+
+       /* write out dir_dscr (e)fe */
+       udf_write_dscr_virt(dir_dscr, udf_rw32(dir_icb->loc.lb_num),
+                       udf_rw16(dir_icb->loc.part_num), 1);
+
+       free(dirdata);
+       free(dir_dscr);
+       return retval;
+}
+
+
+static int
+udf_populate(const char *dir, fsnode *root, fsinfo_t *fsopts,
+               struct udf_stats *stats)
+{
+       struct long_ad rooticb;
+       static char path[MAXPATHLEN+1];
+       int error;
+
+       /* make sure the root gets the rootdir entry */
+       context.metadata_alloc_pos = layout.rootdir;
+       context.data_alloc_pos = layout.rootdir;
+
+       strncpy(path, dir, sizeof(path));
+       error = udf_populate_walk(fsopts, root, path, &rooticb, &rooticb);
+
+       return error;
+}
+
+
+static void
+udf_enumerate_and_estimate(const char *dir, fsnode *root, fsinfo_t *fsopts,
+               struct udf_stats *stats)
+{
+       char path[MAXPATHLEN + 1];
+       off_t proposed_size;
+       uint32_t n, nblk;
+
+       strncpy(path, dir, sizeof(path));
+
+       /* calculate strict minimal size */
+       udf_estimate_walk(fsopts, root, path, stats);
+       printf("ndirs            %d\n", stats->ndirs);
+       printf("nfiles           %d\n", stats->nfiles);
+       printf("ndata_blocks     %d\n", stats->ndatablocks);
+       printf("nmetadata_blocks %d\n", stats->nmetadatablocks);
+       printf("\n");
+
+       /* adjust for options : free file nodes */
+       if (fsopts->freefiles) {
+               /* be mercifull and reserve more for the FID */
+               stats->nmetadatablocks += fsopts->freefiles * 1.5;
+       } else if ((n = fsopts->freefilepc)) {
+               stats->nmetadatablocks += (stats->nmetadatablocks*n) / (100-n);
+       }
+
+       /* adjust for options : free data blocks */
+       if (fsopts->freeblocks) {
+               stats->ndatablocks += fsopts->freeblocks;
+       } else if ((n = fsopts->freeblockpc)) {
+               stats->ndatablocks += (stats->ndatablocks * n) / (100-n);
+       }
+
+       /* rough predictor of minimum disc size */
+       nblk  = stats->ndatablocks + stats->nmetadatablocks;
+       nblk = (double) nblk * (1.0 + 1.0/8.0);         /* free space map    */
+       nblk += 256;                                    /* pre-volume space  */
+       nblk += 256;                                    /* post-volume space */
+       nblk += 64;                                     /* safeguard         */
+
+       /* try to honour minimum size */
+       n = fsopts->minsize / fsopts->sectorsize;
+       if (nblk < n) {
+               stats->ndatablocks += (n - nblk);
+               nblk += n - nblk;
+       }
+       proposed_size = (off_t) nblk * fsopts->sectorsize;
+       /* sanity size */
+       if (proposed_size < 512*1024)
+               proposed_size = 512*1024;
+
+       if (fsopts->size) {
+               if (fsopts->size < proposed_size) 
+                       err(EINVAL, "makefs_udf: won't fit on disc!");
+       } else {
+               fsopts->size = proposed_size;
+       }
+
+       fsopts->inodes = stats->nfiles + stats->ndirs;
+}
+
+
+void
+udf_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+       struct udf_stats stats;
+       uint64_t truncate_len;
+       char scrap[255];
+       int error;
+
+       /* determine format */
+       udf_emulate_discinfo(fsopts, &mmc_discinfo, mmc_profile);
+       printf("req_enable %d, req_disable %d\n", req_enable, req_disable);
+
+       context.sector_size = fsopts->sectorsize;
+       error = udf_derive_format(req_enable, req_disable, false);
+       if (error)
+               err(EINVAL, "makefs_udf: can't determine format");
+
+       /* names */
+       error = udf_proces_names();
+       if (error)
+               err(EINVAL, "makefs_udf: bad names given");
+
+       /* set return value to 1 indicating error */
+       error = 1;
+
+       /* estimate the amount of space needed */
+       memset(&stats, 0, sizeof(stats));
+       udf_enumerate_and_estimate(dir, root, fsopts, &stats);
+
+       printf("Calculated size of `%s': %lld bytes, %ld inodes\n",
+           image, (long long)fsopts->size, (long)fsopts->inodes);
+
+       /* create file image */
+       if ((fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) {
+               err(EXIT_FAILURE, "%s", image);
+       }
+       if (lseek(fd, fsopts->size - 1, SEEK_SET) == -1) {
+               goto err_exit;
+       }
+       if (write(fd, &fd, 1) != 1) {
+               goto err_exit;
+       }
+       if (lseek(fd, 0, SEEK_SET) == -1) {
+               goto err_exit;
+       }
+       fsopts->fd = fd;
+
+       /* calculate metadata percentage */
+       meta_fract = fsopts->size / (stats.nmetadatablocks*fsopts->sectorsize);
+       meta_fract = ((int) ((meta_fract + 0.005)*100.0)) / 100;
+
+       /* update mmc info but now with correct size */
+       udf_emulate_discinfo(fsopts, &mmc_discinfo, mmc_profile);
+
+       printf("Building disc compatible with UDF version %x to %x\n\n",
+               context.min_udf, context.max_udf);
+       (void)snprintb(scrap, sizeof(scrap), FORMAT_FLAGBITS,
+           (uint64_t) format_flags);
+       printf("UDF properties       %s\n", scrap);
+       printf("Volume set          `%s'\n", context.volset_name);
+       printf("Primary volume      `%s`\n", context.primary_name);
+       printf("Logical volume      `%s`\n", context.logvol_name);
+       if (format_flags & FORMAT_META)
+               printf("Metadata percentage  %d %%\n",
+                       (int) (100.0*stats.ndatablocks/stats.nmetadatablocks));
+       printf("\n");
+       udf_do_newfs_prefix();
+
+       /* update context */
+       context.unique_id = 0;
+
+       /* XXX are the next two needed? or should be re-count them? */
+       context.num_files = stats.nfiles;
+       context.num_directories = stats.ndirs;
+
+       error = udf_populate(dir, root, fsopts, &stats);
+
+       udf_do_newfs_postfix();
+
+       if (format_flags & FORMAT_VAT) {
+               truncate_len = context.vtop_offset[context.data_part] +
+                       context.data_alloc_pos;
+               truncate_len *= context.sector_size;
+
+               printf("\nTruncing the disc-image to allow for VAT\n");
+               printf("Free space left on this volume approx. "
+                       "%"PRIu64" KiB, %"PRIu64" MiB\n",
+                       (fsopts->size - truncate_len)/1024,
+                       (fsopts->size - truncate_len)/1024/1024);
+               ftruncate(fd, truncate_len);
+       }
+
+       if (error) {
+               error = 2;      /* some files couldn't be added */
+               goto err_exit;
+       }
+
+       close(fd);
+       return;
+
+err_exit:
+       close(fd);
+       if (error == 2) {
+               errx(error, "Not all files could be added");
+       } else {
+               errx(error, "creation of %s failed", image);
+       }
+}
+
diff --git a/usr.sbin/makefs/udf/Makefile.inc b/usr.sbin/makefs/udf/Makefile.inc
new file mode 100644 (file)
index 0000000..8d47e9b
--- /dev/null
@@ -0,0 +1,13 @@
+#      $NetBSD: Makefile.inc,v 1.2 2013/08/05 18:45:00 reinoud Exp $
+#
+
+UDF=   ${NETBSDSRCDIR}/sys/fs/udf
+UDF_NEWFS=     ${NETBSDSRCDIR}/sbin/newfs_udf
+FSCK=  ${NETBSDSRCDIR}/sbin/fsck       # use progress meter.
+
+.PATH: ${.CURDIR}/udf ${UDF} ${UDF_NEWFS} ${FSCK}
+
+CPPFLAGS+= -I${UDF} -I${UDF_NEWFS} -I${FSCK}
+
+SRCS += udf_create.c udf_write.c udf_osta.c
+
diff --git a/usr.sbin/makefs/udf/cdio_mmc_structs.h b/usr.sbin/makefs/udf/cdio_mmc_structs.h
new file mode 100644 (file)
index 0000000..8e5992d
--- /dev/null
@@ -0,0 +1,163 @@
+/* $NetBSD: cdio_mmc_structs.h,v 1.1 2013/08/05 18:44:16 reinoud Exp $ */
+
+/*
+ * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk
+ * 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ * 
+ */
+
+#ifndef _CDIO_MMC_EMU_H_
+#define _CDIO_MMC_EMU_H_
+
+#include <sys/types.h>
+
+/*
+ * MMC device abstraction interface.
+ *
+ * It gathers information from GET_CONFIGURATION, READ_DISCINFO,
+ * READ_TRACKINFO, READ_TOC2, READ_CD_CAPACITY and GET_CONFIGURATION
+ * SCSI/ATAPI calls regardless if its a legacy CD-ROM/DVD-ROM device or a MMC
+ * standard recordable device.
+ */
+struct mmc_discinfo {
+       uint16_t        mmc_profile;
+       uint16_t        mmc_class;
+
+       uint8_t         disc_state;
+       uint8_t         last_session_state;
+       uint8_t         bg_format_state;
+       uint8_t         link_block_penalty;     /* in sectors              */
+
+       uint64_t        mmc_cur;                /* current MMC_CAPs        */
+       uint64_t        mmc_cap;                /* possible MMC_CAPs       */
+
+       uint32_t        disc_flags;             /* misc flags              */
+
+       uint32_t        disc_id;
+       uint64_t        disc_barcode;
+       uint8_t         application_code;       /* 8 bit really            */
+
+       uint8_t         unused1[3];             /* padding                 */
+
+       uint32_t        last_possible_lba;      /* last leadout start adr. */
+       uint32_t        sector_size;
+
+       uint16_t        num_sessions;
+       uint16_t        num_tracks;             /* derived */
+
+       uint16_t        first_track;
+       uint16_t        first_track_last_session;
+       uint16_t        last_track_last_session;
+
+       uint16_t        unused2;                /* padding/misc info resv. */
+
+       uint16_t        reserved1[4];           /* MMC-5 track resources   */
+       uint32_t        reserved2[3];           /* MMC-5 POW resources     */
+
+       uint32_t        reserved3[8];           /* MMC-5+ */
+};
+#define MMCGETDISCINFO _IOR('c', 28, struct mmc_discinfo)
+
+#define MMC_CLASS_UNKN  0
+#define MMC_CLASS_DISC 1
+#define MMC_CLASS_CD   2
+#define MMC_CLASS_DVD  3
+#define MMC_CLASS_MO   4
+#define MMC_CLASS_BD   5
+#define MMC_CLASS_FILE 0xffff  /* emulation mode */
+
+#define MMC_DFLAGS_BARCODEVALID        (1 <<  0)  /* barcode is present and valid   */
+#define MMC_DFLAGS_DISCIDVALID  (1 <<  1)  /* discid is present and valid    */
+#define MMC_DFLAGS_APPCODEVALID (1 <<  2)  /* application code valid         */
+#define MMC_DFLAGS_UNRESTRICTED (1 <<  3)  /* restricted, then set app. code */
+
+#define MMC_DFLAGS_FLAGBITS \
+    "\10\1BARCODEVALID\2DISCIDVALID\3APPCODEVALID\4UNRESTRICTED"
+
+#define MMC_CAP_SEQUENTIAL     (1 <<  0)  /* sequential writable only       */
+#define MMC_CAP_RECORDABLE     (1 <<  1)  /* record-able; i.e. not static   */
+#define MMC_CAP_ERASABLE       (1 <<  2)  /* drive can erase sectors        */
+#define MMC_CAP_BLANKABLE      (1 <<  3)  /* media can be blanked           */
+#define MMC_CAP_FORMATTABLE    (1 <<  4)  /* media can be formatted         */
+#define MMC_CAP_REWRITABLE     (1 <<  5)  /* media can be rewritten         */
+#define MMC_CAP_MRW            (1 <<  6)  /* Mount Rainier formatted        */
+#define MMC_CAP_PACKET         (1 <<  7)  /* using packet recording         */
+#define MMC_CAP_STRICTOVERWRITE        (1 <<  8)  /* only writes a packet at a time */
+#define MMC_CAP_PSEUDOOVERWRITE (1 <<  9)  /* overwrite through replacement  */
+#define MMC_CAP_ZEROLINKBLK    (1 << 10)  /* zero link block length capable */
+#define MMC_CAP_HW_DEFECTFREE  (1 << 11)  /* hardware defect management     */
+
+#define MMC_CAP_FLAGBITS \
+    "\10\1SEQUENTIAL\2RECORDABLE\3ERASABLE\4BLANKABLE\5FORMATTABLE" \
+    "\6REWRITABLE\7MRW\10PACKET\11STRICTOVERWRITE\12PSEUDOOVERWRITE" \
+    "\13ZEROLINKBLK\14HW_DEFECTFREE"
+
+#define MMC_STATE_EMPTY                0
+#define MMC_STATE_INCOMPLETE   1
+#define MMC_STATE_FULL         2
+#define MMC_STATE_CLOSED       3
+
+#define MMC_BGFSTATE_UNFORM    0
+#define MMC_BGFSTATE_STOPPED   1
+#define MMC_BGFSTATE_RUNNING   2
+#define        MMC_BGFSTATE_COMPLETED  3
+
+
+struct mmc_trackinfo {
+       uint16_t        tracknr;        /* IN/OUT */
+       uint16_t        sessionnr;
+
+       uint8_t         track_mode;
+       uint8_t         data_mode;
+
+       uint16_t        flags;
+
+       uint32_t        track_start;
+       uint32_t        next_writable;
+       uint32_t        free_blocks;
+       uint32_t        packet_size;
+       uint32_t        track_size;
+       uint32_t        last_recorded;
+};
+#define MMCGETTRACKINFO        _IOWR('c', 29, struct mmc_trackinfo)
+
+#define MMC_TRACKINFO_COPY             (1 <<  0)
+#define MMC_TRACKINFO_DAMAGED          (1 <<  1)
+#define MMC_TRACKINFO_FIXED_PACKET     (1 <<  2)
+#define MMC_TRACKINFO_INCREMENTAL      (1 <<  3)
+#define MMC_TRACKINFO_BLANK            (1 <<  4)
+#define MMC_TRACKINFO_RESERVED         (1 <<  5)
+#define MMC_TRACKINFO_NWA_VALID                (1 <<  6)
+#define MMC_TRACKINFO_LRA_VALID                (1 <<  7)
+#define MMC_TRACKINFO_DATA             (1 <<  8)
+#define MMC_TRACKINFO_AUDIO            (1 <<  9)
+#define MMC_TRACKINFO_AUDIO_4CHAN      (1 << 10)
+#define MMC_TRACKINFO_PRE_EMPH         (1 << 11)
+
+#define MMC_TRACKINFO_FLAGBITS \
+    "\10\1COPY\2DAMAGED\3FIXEDPACKET\4INCREMENTAL\5BLANK" \
+    "\6RESERVED\7NWA_VALID\10LRA_VALID\11DATA\12AUDIO" \
+    "\13AUDIO_4CHAN\14PRE_EMPH"
+
+#endif /* _CDIO_MMC_EMU_H_ */
+
diff --git a/usr.sbin/makefs/v7fs.c b/usr.sbin/makefs/v7fs.c
new file mode 100644 (file)
index 0000000..7c479a3
--- /dev/null
@@ -0,0 +1,191 @@
+/*     $NetBSD: v7fs.c,v 1.8 2013/01/29 15:52:25 christos Exp $        */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: v7fs.c,v 1.8 2013/01/29 15:52:25 christos Exp $");
+#endif /* !__lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_makefs.h"
+#include "newfs_v7fs.h"
+
+
+#ifndef HAVE_NBTOOL_CONFIG_H
+#include "progress.h"
+static bool progress_bar_enable;
+#endif
+int v7fs_newfs_verbose;
+
+void
+v7fs_prep_opts(fsinfo_t *fsopts)
+{
+       v7fs_opt_t *v7fs_opts = ecalloc(1, sizeof(*v7fs_opts));
+       const option_t v7fs_options[] = {
+               { 'p', "pdp", &v7fs_opts->pdp_endian, OPT_INT32, false, true,
+                   "PDP endian" },
+               { 'P', "progress", &v7fs_opts->progress, OPT_INT32, false, true,
+                 "Progress bar" },
+               { .name = NULL }
+       };
+
+       fsopts->fs_specific = v7fs_opts;
+       fsopts->fs_options = copy_opts(v7fs_options);
+}
+
+void
+v7fs_cleanup_opts(fsinfo_t *fsopts)
+{
+       free(fsopts->fs_specific);
+       free(fsopts->fs_options);
+}
+
+int
+v7fs_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+
+       return set_option_var(fsopts->fs_options, option, "1", NULL, 0) != -1;
+}
+
+void
+v7fs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+       struct v7fs_mount_device v7fs_mount;
+       int fd, endian, error = 1;
+       v7fs_opt_t *v7fs_opts = fsopts->fs_specific;
+
+       v7fs_newfs_verbose = debug;
+#ifndef HAVE_NBTOOL_CONFIG_H
+       if ((progress_bar_enable = v7fs_opts->progress)) {
+               progress_switch(progress_bar_enable);
+               progress_init();
+               progress(&(struct progress_arg){ .cdev = image });
+       }
+#endif
+
+       /* Determine filesystem image size */
+       v7fs_estimate(dir, root, fsopts);
+       printf("Calculated size of `%s': %lld bytes, %ld inodes\n",
+           image, (long long)fsopts->size, (long)fsopts->inodes);
+
+       if ((fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) {
+               err(EXIT_FAILURE, "%s", image);
+       }
+       if (lseek(fd, fsopts->size - 1, SEEK_SET) == -1) {
+               goto err_exit;
+       }
+       if (write(fd, &fd, 1) != 1) {
+               goto err_exit;
+       }
+       if (lseek(fd, 0, SEEK_SET) == -1) {
+               goto err_exit;
+       }
+       fsopts->fd = fd;
+       v7fs_mount.device.fd = fd;
+
+#if !defined BYTE_ORDER
+#error
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+       if (fsopts->needswap)
+               endian = BIG_ENDIAN;
+       else
+               endian = LITTLE_ENDIAN;
+#else
+       if (fsopts->needswap)
+               endian = LITTLE_ENDIAN;
+       else
+               endian = BIG_ENDIAN;
+#endif
+       if (v7fs_opts->pdp_endian) {
+               endian = PDP_ENDIAN;
+       }
+
+       v7fs_mount.endian = endian;
+       v7fs_mount.sectors = fsopts->size >> V7FS_BSHIFT;
+       if (v7fs_newfs(&v7fs_mount, fsopts->inodes) != 0) {
+               goto err_exit;
+       }
+
+       if (v7fs_populate(dir, root, fsopts, &v7fs_mount) != 0) {
+               error = 2;      /* some files couldn't add */
+               goto err_exit;
+       }
+
+       close(fd);
+       return;
+
+ err_exit:
+       close(fd);
+       err(error, "%s", image);
+}
+
+void
+progress(const struct progress_arg *p)
+{
+#ifndef HAVE_NBTOOL_CONFIG_H
+       static struct progress_arg Progress;
+       static char cdev[32];
+       static char label[32];
+
+       if (!progress_bar_enable)
+               return;
+
+       if (p) {
+               Progress = *p;
+               if (p->cdev)
+                       strcpy(cdev, p->cdev);
+               if (p->label)
+                       strcpy(label, p->label);
+       }
+
+       if (!Progress.tick)
+               return;
+       if (++Progress.cnt > Progress.tick) {
+               Progress.cnt = 0;
+               Progress.total++;
+               progress_bar(cdev, label, Progress.total, PROGRESS_BAR_GRANULE);
+       }
+#endif
+}
diff --git a/usr.sbin/makefs/v7fs/Makefile.inc b/usr.sbin/makefs/v7fs/Makefile.inc
new file mode 100644 (file)
index 0000000..6c8163d
--- /dev/null
@@ -0,0 +1,20 @@
+#      $NetBSD: Makefile.inc,v 1.5 2013/01/23 21:03:15 christos Exp $
+#
+
+V7FS=  ${NETBSDSRCDIR}/sys/fs/v7fs
+V7FS_NEWFS=    ${NETBSDSRCDIR}/sbin/newfs_v7fs
+FSCK=  ${NETBSDSRCDIR}/sbin/fsck       # use progress meter.
+
+.PATH: ${.CURDIR}/v7fs ${V7FS} ${V7FS_NEWFS} ${FSCK}
+
+CPPFLAGS+= -DV7FS_EI -I${V7FS} -I${V7FS_NEWFS} -I${FSCK}
+
+SRCS += v7fs_endian.c v7fs_superblock.c v7fs_superblock_util.c v7fs_inode.c \
+v7fs_inode_util.c v7fs_datablock.c v7fs_dirent.c v7fs_io.c v7fs_file.c \
+v7fs_file_util.c v7fs_io_user.c
+SRCS += main.c         # newfs
+.if !defined(HOSTPROG)
+SRCS += progress.c     # progress bar (fsck)
+.endif
+
+SRCS += v7fs_estimate.c v7fs_populate.c
diff --git a/usr.sbin/makefs/v7fs/v7fs_estimate.c b/usr.sbin/makefs/v7fs/v7fs_estimate.c
new file mode 100644 (file)
index 0000000..74b7e25
--- /dev/null
@@ -0,0 +1,275 @@
+/*     $NetBSD: v7fs_estimate.c,v 1.3 2012/04/19 17:28:26 christos Exp $       */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: v7fs_estimate.c,v 1.3 2012/04/19 17:28:26 christos Exp $");
+#endif /* !__lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/mount.h> /*MAXPATHLEN */
+#endif
+
+#include "makefs.h"
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_inode.h"
+#include "v7fs_datablock.h"
+#include "v7fs_makefs.h"
+
+struct v7fs_geometry {
+       v7fs_daddr_t ndatablock;
+       v7fs_ino_t ninode;
+       v7fs_daddr_t npuredatablk;
+};
+
+#define        VPRINTF(fmt, args...)   { if (v7fs_newfs_verbose) printf(fmt, ##args); }
+
+static int
+v7fs_datablock_size(off_t sz, v7fs_daddr_t *nblk)
+{
+       struct v7fs_daddr_map map;
+       int error = 0;
+
+       if (sz == 0) {
+               *nblk = 0;
+               return 0;
+       }
+
+       if ((error = v7fs_datablock_addr(sz, &map))) {
+               return error;
+       }
+       switch (map.level) {
+       case 0: /* Direct */
+               *nblk = map.index[0] + 1;
+               break;
+       case 1:
+               *nblk = V7FS_NADDR_DIRECT +     /*direct */
+                   1/*addr[V7FS_NADDR_INDEX1]*/ + map.index[0] + 1;
+               break;
+       case 2:
+               *nblk = V7FS_NADDR_DIRECT +     /*direct */
+                   1/*addr[V7FS_NADDR_INDEX1]*/ +
+                   V7FS_DADDR_PER_BLOCK +/*idx1 */
+                   1/*addr[V7FS_NADDR_INDEX2]*/ +
+                   map.index[0] + /* # of idx2 index block(filled) */
+                   map.index[0] * V7FS_DADDR_PER_BLOCK + /* of its datablocks*/
+                   1 + /*current idx2 indexblock */
+                   map.index[1] + 1;
+               break;
+       case 3:
+               *nblk = V7FS_NADDR_DIRECT +     /*direct */
+                   1/*addr[V7FS_NADDR_INDEX1]*/ +
+                   V7FS_DADDR_PER_BLOCK +/*idx1 */
+                   1/*addr[V7FS_NADDR_INDEX2]*/ +
+                   V7FS_DADDR_PER_BLOCK + /* # of idx2 index block */
+                   V7FS_DADDR_PER_BLOCK * V7FS_DADDR_PER_BLOCK +
+                   /*idx2 datablk */
+                   1/*addr[v7FS_NADDR_INDEX3*/ +
+                   map.index[0] + /* # of lv1 index block(filled) */
+                   map.index[0] * V7FS_DADDR_PER_BLOCK * V7FS_DADDR_PER_BLOCK +
+                   1 + /*lv1 */
+                   map.index[1] + /* #of lv2 index block(filled) */
+                   map.index[1] * V7FS_DADDR_PER_BLOCK + /*lv2 datablock */
+                   1 + /* current lv2 index block */
+                   map.index[2] + 1; /*filled datablock */
+               break;
+       default:
+               *nblk = 0;
+               error = EINVAL;
+               break;
+       }
+
+       return error;
+}
+
+static int
+estimate_size_walk(fsnode *root, char *dir, struct v7fs_geometry *geom)
+{
+       fsnode *cur;
+       int nentries;
+       size_t pathlen = strlen(dir);
+       char *mydir = dir + pathlen;
+       fsinode *fnode;
+       v7fs_daddr_t nblk;
+       int n;
+       off_t sz;
+
+       for (cur = root, nentries = 0; cur != NULL; cur = cur->next,
+                nentries++, geom->ninode++) {
+               switch (cur->type & S_IFMT) {
+               default:
+                       break;
+               case S_IFDIR:
+                       if (!cur->child)
+                               break;
+                       mydir[0] = '/';
+                       strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen);
+                       n = estimate_size_walk(cur->child, dir, geom);
+                       sz = (n + 1/*..*/) * sizeof(struct v7fs_dirent);
+                       v7fs_datablock_size(sz, &nblk);
+                       mydir[0] = '\0';
+                       geom->ndatablock += nblk;
+                       geom->npuredatablk +=
+                           V7FS_ROUND_BSIZE(sz) >> V7FS_BSHIFT;
+                       break;
+               case S_IFREG:
+                       fnode = cur->inode;
+                       if (!(fnode->flags & FI_SIZED)) { /*Skip hard-link */
+                               sz = fnode->st.st_size;
+                               v7fs_datablock_size(sz, &nblk);
+                               geom->ndatablock += nblk;
+                               geom->npuredatablk +=
+                                   V7FS_ROUND_BSIZE(sz) >> V7FS_BSHIFT;
+                               fnode->flags |= FI_SIZED;
+                       }
+
+                       break;
+               case S_IFLNK:
+                       nblk = V7FSBSD_MAXSYMLINKLEN >> V7FS_BSHIFT;
+                       geom->ndatablock += nblk;
+                       geom->npuredatablk += nblk;
+                       break;
+               }
+       }
+
+       return nentries;
+}
+
+static v7fs_daddr_t
+calculate_fs_size(const struct v7fs_geometry *geom)
+{
+       v7fs_daddr_t fs_blk, ilist_blk;
+
+       ilist_blk = V7FS_ROUND_BSIZE(geom->ninode *
+           sizeof(struct v7fs_inode_diskimage)) >> V7FS_BSHIFT;
+       fs_blk = geom->ndatablock + ilist_blk + V7FS_ILIST_SECTOR;
+
+       VPRINTF("datablock:%d ilistblock:%d total:%d\n", geom->ndatablock,
+           ilist_blk, fs_blk);
+
+       return fs_blk;
+}
+
+static void
+determine_fs_size(fsinfo_t *fsopts, struct v7fs_geometry *geom)
+{
+       v7fs_daddr_t nblk = geom->ndatablock;
+       v7fs_daddr_t fsblk, n;
+       int32_t nfiles = geom->ninode;
+
+       VPRINTF("size=%lld inodes=%lld fd=%d superb=%p onlyspec=%d\n",
+           (long long)fsopts->size, (long long)fsopts->inodes, fsopts->fd,
+           fsopts->superblock, fsopts->onlyspec);
+       VPRINTF("minsize=%lld maxsize=%lld freefiles=%lld freefilepc=%d "
+           "freeblocks=%lld freeblockpc=%d sectorseize=%d\n",
+           (long long)fsopts->minsize, (long long)fsopts->maxsize,
+           (long long)fsopts->freefiles, fsopts->freefilepc,
+           (long long)fsopts->freeblocks,  fsopts->freeblockpc,
+           fsopts->sectorsize);
+
+       if ((fsopts->sectorsize > 0) && (fsopts->sectorsize != V7FS_BSIZE))
+               warnx("v7fs sector size is 512byte only. '-S %d' is ignored.",
+                   fsopts->sectorsize);
+
+       /* Free inode */
+       if (fsopts->freefiles) {
+               nfiles += fsopts->freefiles;
+       } else if ((n = fsopts->freefilepc)) {
+               nfiles += (nfiles * n) / (100 - n);
+       }
+       if (nfiles >= V7FS_INODE_MAX) {
+               errx(EXIT_FAILURE, "# of files(%d) over v7fs limit(%d).",
+                   nfiles, V7FS_INODE_MAX);
+       }
+
+       /* Free datablock */
+       if (fsopts->freeblocks) {
+               nblk += fsopts->freeblocks;
+       } else if ((n = fsopts->freeblockpc)) {
+               nblk += (nblk * n) / (100 - n);
+       }
+
+       /* Total size */
+       geom->ndatablock = nblk;
+       geom->ninode = nfiles;
+       fsblk = calculate_fs_size(geom);
+
+       if (fsblk >= V7FS_DADDR_MAX) {
+               errx(EXIT_FAILURE, "filesystem size(%d) over v7fs limit(%d).",
+                   fsblk, V7FS_DADDR_MAX);
+       }
+
+       n = fsopts->minsize >> V7FS_BSHIFT;
+       if (fsblk < n)
+               geom->ndatablock += (n - fsblk);
+
+       n = fsopts->maxsize >> V7FS_BSHIFT;
+       if (fsopts->maxsize > 0 && fsblk > n) {
+               errx(EXIT_FAILURE, "# of datablocks %d > %d", fsblk, n);
+       }
+}
+
+void
+v7fs_estimate(const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+       v7fs_opt_t *v7fs_opts = fsopts->fs_specific;
+       char path[MAXPATHLEN + 1];
+       int ndir;
+       off_t sz;
+       v7fs_daddr_t nblk;
+       struct v7fs_geometry geom;
+
+       memset(&geom , 0, sizeof(geom));
+       strncpy(path, dir, sizeof(path));
+
+       /* Calculate strict size. */
+       ndir = estimate_size_walk(root, path, &geom);
+       sz = (ndir + 1/*..*/) * sizeof(struct v7fs_dirent);
+       v7fs_datablock_size(sz, &nblk);
+       geom.ndatablock += nblk;
+
+       /* Consider options. */
+       determine_fs_size(fsopts, &geom);
+
+       fsopts->size = calculate_fs_size(&geom) << V7FS_BSHIFT;
+       fsopts->inodes = geom.ninode;
+       v7fs_opts->npuredatablk = geom.npuredatablk; /* for progress bar */
+}
diff --git a/usr.sbin/makefs/v7fs/v7fs_populate.c b/usr.sbin/makefs/v7fs/v7fs_populate.c
new file mode 100644 (file)
index 0000000..5b62158
--- /dev/null
@@ -0,0 +1,298 @@
+/*     $NetBSD: v7fs_populate.c,v 1.3 2011/08/10 11:31:49 uch Exp $    */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: v7fs_populate.c,v 1.3 2011/08/10 11:31:49 uch Exp $");
+#endif /* !__lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/mount.h>
+#endif
+
+#include "makefs.h"
+#include "v7fs.h"
+#include "v7fs_impl.h"
+#include "v7fs_inode.h"
+#include "v7fs_superblock.h"
+#include "v7fs_datablock.h"
+#include "v7fs_endian.h"
+#include "v7fs_file.h"
+#include "v7fs_makefs.h"
+#include "newfs_v7fs.h"
+
+#define        VPRINTF(fmt, args...)   { if (v7fs_newfs_verbose) printf(fmt, ##args); }
+
+static void
+attr_setup(fsnode *node, struct v7fs_fileattr *attr)
+{
+       struct stat *st = &node->inode->st;
+
+       attr->mode = node->type | st->st_mode;
+       attr->uid = st->st_uid;
+       attr->gid = st->st_gid;
+       attr->device = 0;
+       attr->ctime = st->st_ctime;
+       attr->atime = st->st_atime;
+       attr->mtime = st->st_mtime;
+}
+
+static int
+allocate(struct v7fs_self *fs, struct v7fs_inode *parent_inode, fsnode *node,
+    dev_t dev, struct v7fs_inode *inode)
+{
+       int error;
+       v7fs_ino_t ino;
+       struct v7fs_fileattr attr;
+
+       memset(inode, 0, sizeof(*inode));
+
+       attr_setup(node, &attr);
+       attr.device = dev;
+       if ((error = v7fs_file_allocate(fs, parent_inode, node->name, &attr,
+           &ino))) {
+               errno = error;
+               warn("%s", node->name);
+               return error;
+       }
+       node->inode->ino = ino;
+       node->inode->flags |= FI_ALLOCATED;
+       if ((error = v7fs_inode_load(fs, inode, ino))) {
+               errno = error;
+               warn("%s", node->name);
+               return error;
+       }
+
+       return 0;
+}
+
+struct copy_arg {
+       int fd;
+       uint8_t buf[V7FS_BSIZE];
+};
+
+static int
+copy_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz)
+{
+       struct copy_arg *p = ctx;
+
+       if (read(p->fd, p->buf, sz) != (ssize_t)sz) {
+               return V7FS_ITERATOR_ERROR;
+       }
+
+       if (!fs->io.write(fs->io.cookie, p->buf, blk)) {
+               errno = EIO;
+               return V7FS_ITERATOR_ERROR;
+       }
+       progress(0);
+
+       return 0;
+}
+
+static int
+file_copy(struct v7fs_self *fs, struct v7fs_inode *parent_inode, fsnode *node,
+       const char *filepath)
+{
+       struct v7fs_inode inode;
+       const char *errmsg;
+       fsinode *fnode = node->inode;
+       int error = 0;
+       int fd;
+
+       /* Check hard-link */
+       if ((fnode->nlink > 1) && (fnode->flags & FI_ALLOCATED)) {
+               if ((error = v7fs_inode_load(fs, &inode, fnode->ino))) {
+                       errmsg = "inode load";
+                       goto err_exit;
+               }
+               if ((error = v7fs_file_link(fs, parent_inode, &inode,
+                           node->name))) {
+                       errmsg = "hard link";
+                       goto err_exit;
+               }
+               return 0;
+       }
+
+       /* Allocate file */
+       if ((error = allocate(fs, parent_inode, node, 0, &inode))) {
+               errmsg = "file allocate";
+               goto err_exit;
+       }
+       if ((error = v7fs_datablock_expand(fs, &inode, fnode->st.st_size))) {
+               errmsg = "datablock expand";
+               goto err_exit;
+       }
+
+       /* Data copy */
+       if ((fd = open(filepath, O_RDONLY)) == -1) {
+               error = errno;
+               errmsg = "source file";
+               goto err_exit;
+       }
+
+       error = v7fs_datablock_foreach(fs, &inode, copy_subr,
+           &(struct copy_arg){ .fd = fd });
+       if (error != V7FS_ITERATOR_END) {
+               errmsg = "data copy";
+               close(fd);
+               goto err_exit;
+       } else {
+               error = 0;
+       }
+       close(fd);
+
+       return error;
+
+err_exit:
+       errno = error;
+       warn("%s %s", node->name, errmsg);
+
+       return error;
+}
+
+static int
+populate_walk(struct v7fs_self *fs, struct v7fs_inode *parent_inode,
+    fsnode *root, char *dir)
+{
+       fsnode *cur;
+       char *mydir = dir + strlen(dir);
+       char srcpath[MAXPATHLEN + 1];
+       struct v7fs_inode inode;
+       int error = 0;
+       bool failed = false;
+
+       for (cur = root; cur != NULL; cur = cur->next) {
+               switch (cur->type & S_IFMT) {
+               default:
+                       VPRINTF("%x\n", cur->flags & S_IFMT);
+                       break;
+               case S_IFCHR:
+                       /*FALLTHROUGH*/
+               case S_IFBLK:
+                       if ((error = allocate(fs, parent_inode, cur,
+                           cur->inode->st.st_rdev, &inode))) {
+                               errno = error;
+                               warn("%s", cur->name);
+                       }
+                       break;
+               case S_IFDIR:
+                       if (!cur->child)        /*'.'*/
+                               break;
+                       /* Allocate this directory. */
+                       if ((error = allocate(fs, parent_inode, cur, 0,
+                           &inode))) {
+                               errno = error;
+                               warn("%s", cur->name);
+                       } else {
+                               /* Populate children. */
+                               mydir[0] = '/';
+                               strcpy(&mydir[1], cur->name);
+                               error = populate_walk(fs, &inode, cur->child,
+                                   dir);
+                               mydir[0] = '\0';
+                       }
+                       break;
+               case S_IFREG:
+                       snprintf(srcpath, sizeof(srcpath), "%s/%s", dir,
+                           cur->name);
+                       error = file_copy(fs, parent_inode, cur, srcpath);
+                       break;
+               case S_IFLNK:
+                       if ((error = allocate(fs, parent_inode, cur, 0,
+                           &inode))) {
+                               errno = error;
+                               warn("%s", cur->name);
+                       } else {
+                               v7fs_file_symlink(fs, &inode, cur->symlink);
+                       }
+                       break;
+               }
+               if (error)
+                       failed = true;
+       }
+
+       return failed ? 2 : 0;
+}
+
+int
+v7fs_populate(const char *dir, fsnode *root, fsinfo_t *fsopts,
+    const struct v7fs_mount_device *device)
+{
+       v7fs_opt_t *v7fs_opts = fsopts->fs_specific;
+       static char path[MAXPATHLEN + 1];
+       struct v7fs_inode root_inode;
+       struct v7fs_self *fs;
+       int error;
+
+       if ((error = v7fs_io_init(&fs, device, V7FS_BSIZE))) {
+               errno = error;
+               warn("I/O setup failed.");
+               return error;
+       }
+       fs->endian = device->endian;
+       v7fs_endian_init(fs);
+
+       if ((error = v7fs_superblock_load(fs))) {
+               errno = error;
+               warn("Can't load superblock.");
+               return error;
+       }
+       fsopts->superblock = &fs->superblock;   /* not used. */
+
+       if ((error = v7fs_inode_load(fs, &root_inode, V7FS_ROOT_INODE))) {
+               errno = error;
+               warn("Can't load root inode.");
+               return error;
+       }
+
+       progress(&(struct progress_arg){ .label = "populate", .tick =
+           v7fs_opts->npuredatablk / PROGRESS_BAR_GRANULE });
+
+       strncpy(path, dir, sizeof(path));
+       error = populate_walk(fs, &root_inode, root, path);
+
+       v7fs_inode_writeback(fs, &root_inode);
+       v7fs_superblock_writeback(fs);
+
+       return error;
+}
diff --git a/usr.sbin/makefs/v7fs_makefs.h b/usr.sbin/makefs/v7fs_makefs.h
new file mode 100644 (file)
index 0000000..1c047ca
--- /dev/null
@@ -0,0 +1,49 @@
+/*     $NetBSD: v7fs_makefs.h,v 1.2 2011/08/10 11:31:49 uch Exp $      */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yasushi.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _MAKEFS_V7FS_H_
+#define        _MAKEFS_V7FS_H_
+
+typedef struct {
+       int pdp_endian;
+       int progress;
+       v7fs_daddr_t npuredatablk;      /* for progress bar */
+} v7fs_opt_t;
+
+__BEGIN_DECLS
+void v7fs_estimate(const char *, fsnode *, fsinfo_t *);
+int v7fs_populate(const char *, fsnode *, fsinfo_t *,
+    const struct v7fs_mount_device *);
+struct progress_arg;
+void progress(const struct progress_arg *);
+extern int v7fs_newfs_verbose;
+__END_DECLS
+#endif /*!_MAKEFS_V7FS_H_*/
diff --git a/usr.sbin/makefs/walk.c b/usr.sbin/makefs/walk.c
new file mode 100644 (file)
index 0000000..5e585ac
--- /dev/null
@@ -0,0 +1,691 @@
+/*     $NetBSD: walk.c,v 1.28 2013/02/03 06:16:53 christos Exp $       */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: walk.c,v 1.28 2013/02/03 06:16:53 christos Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "mtree.h"
+
+static void     apply_specdir(const char *, NODE *, fsnode *, int);
+static void     apply_specentry(const char *, NODE *, fsnode *);
+static fsnode  *create_fsnode(const char *, const char *, const char *,
+                              struct stat *);
+static fsinode *link_check(fsinode *);
+
+
+/*
+ * walk_dir --
+ *     build a tree of fsnodes from `root' and `dir', with a parent
+ *     fsnode of `parent' (which may be NULL for the root of the tree).
+ *     append the tree to a fsnode of `join' if it is not NULL.
+ *     each "level" is a directory, with the "." entry guaranteed to be
+ *     at the start of the list, and without ".." entries.
+ */
+fsnode *
+walk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join,
+    int replace)
+{
+       fsnode          *first, *cur, *prev, *last;
+       DIR             *dirp;
+       struct dirent   *dent;
+       char            path[MAXPATHLEN + 1];
+       struct stat     stbuf;
+       char            *name, *rp;
+       int             dot, len;
+
+       assert(root != NULL);
+       assert(dir != NULL);
+
+       len = snprintf(path, sizeof(path), "%s/%s", root, dir);
+       if (len >= (int)sizeof(path))
+               errx(1, "Pathname too long.");
+       if (debug & DEBUG_WALK_DIR)
+               printf("walk_dir: %s %p\n", path, parent);
+       if ((dirp = opendir(path)) == NULL)
+               err(1, "Can't opendir `%s'", path);
+       rp = path + strlen(root) + 1;
+       if (join != NULL) {
+               first = cur = join;
+               while (cur->next != NULL)
+                       cur = cur->next;
+               prev = last = cur;
+       } else
+               last = first = prev = NULL;
+       while ((dent = readdir(dirp)) != NULL) {
+               name = dent->d_name;
+               dot = 0;
+               if (name[0] == '.')
+                       switch (name[1]) {
+                       case '\0':      /* "." */
+                               if (join != NULL)
+                                       continue;
+                               dot = 1;
+                               break;
+                       case '.':       /* ".." */
+                               if (name[2] == '\0')
+                                       continue;
+                               /* FALLTHROUGH */
+                       default:
+                               dot = 0;
+                       }
+               if (debug & DEBUG_WALK_DIR_NODE)
+                       printf("scanning %s/%s/%s\n", root, dir, name);
+               if (snprintf(path + len, sizeof(path) - len, "/%s", name) >=
+                   (int)sizeof(path) - len)
+                       errx(1, "Pathname too long.");
+               if (lstat(path, &stbuf) == -1)
+                       err(1, "Can't lstat `%s'", path);
+#ifdef S_ISSOCK
+               if (S_ISSOCK(stbuf.st_mode & S_IFMT)) {
+                       if (debug & DEBUG_WALK_DIR_NODE)
+                               printf("  skipping socket %s\n", path);
+                       continue;
+               }
+#endif
+
+               if (join != NULL) {
+                       cur = join->next;
+                       for (;;) {
+                               if (cur == NULL || strcmp(cur->name, name) == 0)
+                                       break;
+                               if (cur == last) {
+                                       cur = NULL;
+                                       break;
+                               }
+                               cur = cur->next;
+                       }
+                       if (cur != NULL) {
+                               if (S_ISDIR(cur->type) &&
+                                   S_ISDIR(stbuf.st_mode)) {
+                                       if (debug & DEBUG_WALK_DIR_NODE)
+                                               printf("merging %s with %p\n",
+                                                   path, cur->child);
+                                       cur->child = walk_dir(root, rp, cur,
+                                           cur->child, replace);
+                                       continue;
+                               }
+                               if (!replace)
+                                       errx(1, "Can't merge %s `%s' with "
+                                           "existing %s",
+                                           inode_type(stbuf.st_mode), path,
+                                           inode_type(cur->type));
+                               else {
+                                       if (debug & DEBUG_WALK_DIR_NODE)
+                                               printf("replacing %s %s\n",
+                                                   inode_type(stbuf.st_mode),
+                                                   path);
+                                       if (cur == join->next)
+                                               join->next = cur->next;
+                                       else {
+                                               fsnode *p;
+                                               for (p = join->next;
+                                                   p->next != cur; p = p->next)
+                                                       continue;
+                                               p->next = cur->next;
+                                       }
+                                       free(cur);
+                               }
+                       }
+               }
+
+               cur = create_fsnode(root, dir, name, &stbuf);
+               cur->parent = parent;
+               if (dot) {
+                               /* ensure "." is at the start of the list */
+                       cur->next = first;
+                       first = cur;
+                       if (! prev)
+                               prev = cur;
+                       cur->first = first;
+               } else {                        /* not "." */
+                       if (prev)
+                               prev->next = cur;
+                       prev = cur;
+                       if (!first)
+                               first = cur;
+                       cur->first = first;
+                       if (S_ISDIR(cur->type)) {
+                               cur->child = walk_dir(root, rp, cur, NULL,
+                                   replace);
+                               continue;
+                       }
+               }
+               if (stbuf.st_nlink > 1) {
+                       fsinode *curino;
+
+                       curino = link_check(cur->inode);
+                       if (curino != NULL) {
+                               free(cur->inode);
+                               cur->inode = curino;
+                               cur->inode->nlink++;
+                               if (debug & DEBUG_WALK_DIR_LINKCHECK)
+                                       printf("link_check: found [%llu, %llu]\n",
+                                           (unsigned long long)curino->st.st_dev,
+                                           (unsigned long long)curino->st.st_ino);
+                       }
+               }
+               if (S_ISLNK(cur->type)) {
+                       char    slink[PATH_MAX+1];
+                       int     llen;
+
+                       llen = readlink(path, slink, sizeof(slink) - 1);
+                       if (llen == -1)
+                               err(1, "Readlink `%s'", path);
+                       slink[llen] = '\0';
+                       cur->symlink = estrdup(slink);
+               }
+       }
+       assert(first != NULL);
+       if (join == NULL)
+               for (cur = first->next; cur != NULL; cur = cur->next)
+                       cur->first = first;
+       if (closedir(dirp) == -1)
+               err(1, "Can't closedir `%s/%s'", root, dir);
+       return (first);
+}
+
+static fsnode *
+create_fsnode(const char *root, const char *path, const char *name,
+    struct stat *stbuf)
+{
+       fsnode *cur;
+
+       cur = ecalloc(1, sizeof(*cur));
+       cur->path = estrdup(path);
+       cur->name = estrdup(name);
+       cur->inode = ecalloc(1, sizeof(*cur->inode));
+       cur->root = root;
+       cur->type = stbuf->st_mode & S_IFMT;
+       cur->inode->nlink = 1;
+       cur->inode->st = *stbuf;
+       return (cur);
+}
+
+/*
+ * free_fsnodes --
+ *     Removes node from tree and frees it and all of
+ *   its decendents.
+ */
+void
+free_fsnodes(fsnode *node)
+{
+       fsnode  *cur, *next;
+
+       assert(node != NULL);
+
+       /* for ".", start with actual parent node */
+       if (node->first == node) {
+               assert(node->name[0] == '.' && node->name[1] == '\0');
+               if (node->parent) {
+                       assert(node->parent->child == node);
+                       node = node->parent;
+               }
+       }
+
+       /* Find ourselves in our sibling list and unlink */
+       if (node->first != node) {
+               for (cur = node->first; cur->next; cur = cur->next) {
+                       if (cur->next == node) {
+                               cur->next = node->next;
+                               node->next = NULL;
+                               break;
+                       }
+               }
+       }
+
+       for (cur = node; cur != NULL; cur = next) {
+               next = cur->next;
+               if (cur->child) {
+                       cur->child->parent = NULL;
+                       free_fsnodes(cur->child);
+               }
+               if (cur->inode->nlink-- == 1)
+                       free(cur->inode);
+               if (cur->symlink)
+                       free(cur->symlink);
+               free(cur->path);
+               free(cur->name);
+               free(cur);
+       }
+}
+
+/*
+ * apply_specfile --
+ *     read in the mtree(8) specfile, and apply it to the tree
+ *     at dir,parent. parameters in parent on equivalent types
+ *     will be changed to those found in specfile, and missing
+ *     entries will be added.
+ */
+void
+apply_specfile(const char *specfile, const char *dir, fsnode *parent, int speconly)
+{
+       struct timeval   start;
+       FILE    *fp;
+       NODE    *root;
+
+       assert(specfile != NULL);
+       assert(parent != NULL);
+
+       if (debug & DEBUG_APPLY_SPECFILE)
+               printf("apply_specfile: %s, %s %p\n", specfile, dir, parent);
+
+                               /* read in the specfile */
+       if ((fp = fopen(specfile, "r")) == NULL)
+               err(1, "Can't open `%s'", specfile);
+       TIMER_START(start);
+       root = spec(fp);
+       TIMER_RESULTS(start, "spec");
+       if (fclose(fp) == EOF)
+               err(1, "Can't close `%s'", specfile);
+
+                               /* perform some sanity checks */
+       if (root == NULL)
+               errx(1, "Specfile `%s' did not contain a tree", specfile);
+       assert(strcmp(root->name, ".") == 0);
+       assert(root->type == F_DIR);
+
+                               /* merge in the changes */
+       apply_specdir(dir, root, parent, speconly);
+
+       free_nodes(root);
+}
+
+static void
+apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode, int speconly)
+{
+       char     path[MAXPATHLEN + 1];
+       NODE    *curnode;
+       fsnode  *curfsnode;
+
+       assert(specnode != NULL);
+       assert(dirnode != NULL);
+
+       if (debug & DEBUG_APPLY_SPECFILE)
+               printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode);
+
+       if (specnode->type != F_DIR)
+               errx(1, "Specfile node `%s/%s' is not a directory",
+                   dir, specnode->name);
+       if (dirnode->type != S_IFDIR)
+               errx(1, "Directory node `%s/%s' is not a directory",
+                   dir, dirnode->name);
+
+       apply_specentry(dir, specnode, dirnode);
+
+       /* Remove any filesystem nodes not found in specfile */
+       /* XXX inefficient.  This is O^2 in each dir and it would
+        * have been better never to have walked this part of the tree
+        * to begin with
+        */
+       if (speconly) {
+               fsnode *next;
+               assert(dirnode->name[0] == '.' && dirnode->name[1] == '\0');
+               for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = next) {
+                       next = curfsnode->next;
+                       for (curnode = specnode->child; curnode != NULL;
+                            curnode = curnode->next) {
+                               if (strcmp(curnode->name, curfsnode->name) == 0)
+                                       break;
+                       }
+                       if (curnode == NULL) {
+                               if (debug & DEBUG_APPLY_SPECONLY) {
+                                       printf("apply_specdir: trimming %s/%s %p\n", dir, curfsnode->name, curfsnode);
+                               }
+                               free_fsnodes(curfsnode);
+                       }
+               }
+       }
+
+                       /* now walk specnode->child matching up with dirnode */
+       for (curnode = specnode->child; curnode != NULL;
+           curnode = curnode->next) {
+               if (debug & DEBUG_APPLY_SPECENTRY)
+                       printf("apply_specdir:  spec %s\n",
+                           curnode->name);
+               for (curfsnode = dirnode->next; curfsnode != NULL;
+                   curfsnode = curfsnode->next) {
+#if 0  /* too verbose for now */
+                       if (debug & DEBUG_APPLY_SPECENTRY)
+                               printf("apply_specdir:  dirent %s\n",
+                                   curfsnode->name);
+#endif
+                       if (strcmp(curnode->name, curfsnode->name) == 0)
+                               break;
+               }
+               if ((size_t)snprintf(path, sizeof(path), "%s/%s",
+                   dir, curnode->name) >= sizeof(path))
+                       errx(1, "Pathname too long.");
+               if (curfsnode == NULL) {        /* need new entry */
+                       struct stat     stbuf;
+
+                                           /*
+                                            * don't add optional spec entries
+                                            * that lack an existing fs entry
+                                            */
+                       if ((curnode->flags & F_OPT) &&
+                           lstat(path, &stbuf) == -1)
+                                       continue;
+
+                                       /* check that enough info is provided */
+#define NODETEST(t, m)                                                 \
+                       if (!(t))                                       \
+                               errx(1, "`%s': %s not provided", path, m)
+                       NODETEST(curnode->flags & F_TYPE, "type");
+                       NODETEST(curnode->flags & F_MODE, "mode");
+                               /* XXX: require F_TIME ? */
+                       NODETEST(curnode->flags & F_GID ||
+                           curnode->flags & F_GNAME, "group");
+                       NODETEST(curnode->flags & F_UID ||
+                           curnode->flags & F_UNAME, "user");
+                       if (curnode->type == F_BLOCK || curnode->type == F_CHAR)
+                               NODETEST(curnode->flags & F_DEV,
+                                   "device number");
+#undef NODETEST
+
+                       if (debug & DEBUG_APPLY_SPECFILE)
+                               printf("apply_specdir: adding %s\n",
+                                   curnode->name);
+                                       /* build minimal fsnode */
+                       memset(&stbuf, 0, sizeof(stbuf));
+                       stbuf.st_mode = nodetoino(curnode->type);
+                       stbuf.st_nlink = 1;
+                       stbuf.st_mtime = stbuf.st_atime =
+                           stbuf.st_ctime = start_time.tv_sec;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+                       stbuf.st_mtimensec = stbuf.st_atimensec =
+                           stbuf.st_ctimensec = start_time.tv_nsec;
+#endif
+                       curfsnode = create_fsnode(".", ".", curnode->name,
+                           &stbuf);
+                       curfsnode->parent = dirnode->parent;
+                       curfsnode->first = dirnode;
+                       curfsnode->next = dirnode->next;
+                       dirnode->next = curfsnode;
+                       if (curfsnode->type == S_IFDIR) {
+                                       /* for dirs, make "." entry as well */
+                               curfsnode->child = create_fsnode(".", ".", ".",
+                                   &stbuf);
+                               curfsnode->child->parent = curfsnode;
+                               curfsnode->child->first = curfsnode->child;
+                       }
+                       if (curfsnode->type == S_IFLNK) {
+                               assert(curnode->slink != NULL);
+                                       /* for symlinks, copy the target */
+                               curfsnode->symlink = estrdup(curnode->slink);
+                       }
+               }
+               apply_specentry(dir, curnode, curfsnode);
+               if (curnode->type == F_DIR) {
+                       if (curfsnode->type != S_IFDIR)
+                               errx(1, "`%s' is not a directory", path);
+                       assert (curfsnode->child != NULL);
+                       apply_specdir(path, curnode, curfsnode->child, speconly);
+               }
+       }
+}
+
+static void
+apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode)
+{
+
+       assert(specnode != NULL);
+       assert(dirnode != NULL);
+
+       if (nodetoino(specnode->type) != dirnode->type)
+               errx(1, "`%s/%s' type mismatch: specfile %s, tree %s",
+                   dir, specnode->name, inode_type(nodetoino(specnode->type)),
+                   inode_type(dirnode->type));
+
+       if (debug & DEBUG_APPLY_SPECENTRY)
+               printf("apply_specentry: %s/%s\n", dir, dirnode->name);
+
+#define ASEPRINT(t, b, o, n) \
+               if (debug & DEBUG_APPLY_SPECENTRY) \
+                       printf("\t\t\tchanging %s from " b " to " b "\n", \
+                           t, o, n)
+
+       if (specnode->flags & (F_GID | F_GNAME)) {
+               ASEPRINT("gid", "%d",
+                   dirnode->inode->st.st_gid, specnode->st_gid);
+               dirnode->inode->st.st_gid = specnode->st_gid;
+       }
+       if (specnode->flags & F_MODE) {
+               ASEPRINT("mode", "%#o",
+                   dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode);
+               dirnode->inode->st.st_mode &= ~ALLPERMS;
+               dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS);
+       }
+               /* XXX: ignoring F_NLINK for now */
+       if (specnode->flags & F_SIZE) {
+               ASEPRINT("size", "%lld",
+                   (long long)dirnode->inode->st.st_size,
+                   (long long)specnode->st_size);
+               dirnode->inode->st.st_size = specnode->st_size;
+       }
+       if (specnode->flags & F_SLINK) {
+               assert(dirnode->symlink != NULL);
+               assert(specnode->slink != NULL);
+               ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink);
+               free(dirnode->symlink);
+               dirnode->symlink = estrdup(specnode->slink);
+       }
+       if (specnode->flags & F_TIME) {
+               ASEPRINT("time", "%ld",
+                   (long)dirnode->inode->st.st_mtime,
+                   (long)specnode->st_mtimespec.tv_sec);
+               dirnode->inode->st.st_mtime =           specnode->st_mtimespec.tv_sec;
+               dirnode->inode->st.st_atime =           specnode->st_mtimespec.tv_sec;
+               dirnode->inode->st.st_ctime =           start_time.tv_sec;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+               dirnode->inode->st.st_mtimensec =       specnode->st_mtimespec.tv_nsec;
+               dirnode->inode->st.st_atimensec =       specnode->st_mtimespec.tv_nsec;
+               dirnode->inode->st.st_ctimensec =       start_time.tv_nsec;
+#endif
+       }
+       if (specnode->flags & (F_UID | F_UNAME)) {
+               ASEPRINT("uid", "%d",
+                   dirnode->inode->st.st_uid, specnode->st_uid);
+               dirnode->inode->st.st_uid = specnode->st_uid;
+       }
+#if HAVE_STRUCT_STAT_ST_FLAGS
+       if (specnode->flags & F_FLAGS) {
+               ASEPRINT("flags", "%#lX",
+                   (unsigned long)dirnode->inode->st.st_flags,
+                   (unsigned long)specnode->st_flags);
+               dirnode->inode->st.st_flags = specnode->st_flags;
+       }
+#endif
+       if (specnode->flags & F_DEV) {
+               ASEPRINT("rdev", "%#llx",
+                   (unsigned long long)dirnode->inode->st.st_rdev,
+                   (unsigned long long)specnode->st_rdev);
+               dirnode->inode->st.st_rdev = specnode->st_rdev;
+       }
+#undef ASEPRINT
+
+       dirnode->flags |= FSNODE_F_HASSPEC;
+}
+
+
+/*
+ * dump_fsnodes --
+ *     dump the fsnodes from `cur'
+ */
+void
+dump_fsnodes(fsnode *root)
+{
+       fsnode  *cur;
+       char    path[MAXPATHLEN + 1];
+
+       printf("dump_fsnodes: %s %p\n", root->path, root);
+       for (cur = root; cur != NULL; cur = cur->next) {
+               if (snprintf(path, sizeof(path), "%s/%s", cur->path,
+                   cur->name) >= (int)sizeof(path))
+                       errx(1, "Pathname too long.");
+
+               if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
+                       printf("cur=%8p parent=%8p first=%8p ",
+                           cur, cur->parent, cur->first);
+               printf("%7s: %s", inode_type(cur->type), path);
+               if (S_ISLNK(cur->type)) {
+                       assert(cur->symlink != NULL);
+                       printf(" -> %s", cur->symlink);
+               } else {
+                       assert (cur->symlink == NULL);
+               }
+               if (cur->inode->nlink > 1)
+                       printf(", nlinks=%d", cur->inode->nlink);
+               putchar('\n');
+
+               if (cur->child) {
+                       assert (cur->type == S_IFDIR);
+                       dump_fsnodes(cur->child);
+               }
+       }
+       printf("dump_fsnodes: finished %s/%s\n", root->path, root->name);
+}
+
+
+/*
+ * inode_type --
+ *     for a given inode type `mode', return a descriptive string.
+ *     for most cases, uses inotype() from mtree/misc.c
+ */
+const char *
+inode_type(mode_t mode)
+{
+
+       if (S_ISLNK(mode))
+               return ("symlink");     /* inotype() returns "link"...  */
+       return (inotype(mode));
+}
+
+
+/*
+ * link_check --
+ *     return pointer to fsinode matching `entry's st_ino & st_dev if it exists,
+ *     otherwise add `entry' to table and return NULL
+ */
+/* This was borrowed from du.c and tweaked to keep an fsnode 
+ * pointer instead. -- dbj@netbsd.org
+ */
+static fsinode *
+link_check(fsinode *entry)
+{
+       static struct entry {
+               fsinode *data;
+       } *htable;
+       static int htshift;  /* log(allocated size) */
+       static int htmask;   /* allocated size - 1 */
+       static int htused;   /* 2*number of insertions */
+       int h, h2;
+       uint64_t tmp;
+       /* this constant is (1<<64)/((1+sqrt(5))/2)
+        * aka (word size)/(golden ratio)
+        */
+       const uint64_t HTCONST = 11400714819323198485ULL;
+       const int HTBITS = 64;
+       
+       /* Never store zero in hashtable */
+       assert(entry);
+
+       /* Extend hash table if necessary, keep load under 0.5 */
+       if (htused<<1 >= htmask) {
+               struct entry *ohtable;
+
+               if (!htable)
+                       htshift = 10;   /* starting hashtable size */
+               else
+                       htshift++;   /* exponential hashtable growth */
+
+               htmask  = (1 << htshift) - 1;
+               htused = 0;
+
+               ohtable = htable;
+               htable = ecalloc(htmask+1, sizeof(*htable));
+               /* populate newly allocated hashtable */
+               if (ohtable) {
+                       int i;
+                       for (i = 0; i <= htmask>>1; i++)
+                               if (ohtable[i].data)
+                                       link_check(ohtable[i].data);
+                       free(ohtable);
+               }
+       }
+
+       /* multiplicative hashing */
+       tmp = entry->st.st_dev;
+       tmp <<= HTBITS>>1;
+       tmp |=  entry->st.st_ino;
+       tmp *= HTCONST;
+       h  = tmp >> (HTBITS - htshift);
+       h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */
+       
+       /* open address hashtable search with double hash probing */
+       while (htable[h].data) {
+               if ((htable[h].data->st.st_ino == entry->st.st_ino) &&
+                   (htable[h].data->st.st_dev == entry->st.st_dev)) {
+                       return htable[h].data;
+               }
+               h = (h + h2) & htmask;
+       }
+
+       /* Insert the current entry into hashtable */
+       htable[h].data = entry;
+       htused++;
+       return NULL;
+}