diff -ruPx *.orig ../src-org/sbin/Makefile sbin/Makefile
--- ../src-org/sbin/Makefile	2020-01-10 21:19:51.595353000 +0900
+++ sbin/Makefile	2019-07-01 16:11:48.892428000 +0900
@@ -47,6 +47,7 @@
 	mount_nfs \
 	mount_nullfs \
 	mount_udf \
+	mount_udf2 \
 	mount_unionfs \
 	newfs \
 	newfs_msdos \
diff -ruPx *.orig ../src-org/sbin/mount/mount.c sbin/mount/mount.c
--- ../src-org/sbin/mount/mount.c	2020-01-10 21:19:55.543636000 +0900
+++ sbin/mount/mount.c	2019-10-01 12:06:59.078609000 +0900
@@ -150,7 +150,7 @@
 	unsigned int i;
 	const char *fs[] = {
 	"cd9660", "mfs", "msdosfs", "nfs",
-	"nullfs", "smbfs", "udf", "unionfs",
+	"nullfs", "smbfs", "udf", "udf2", "unionfs",
 	NULL
 	};
 
diff -ruPx *.orig ../src-org/sbin/mount_udf2/Makefile sbin/mount_udf2/Makefile
--- ../src-org/sbin/mount_udf2/Makefile	1970-01-01 09:00:00.000000000 +0900
+++ sbin/mount_udf2/Makefile	2018-10-05 09:56:25.602212000 +0900
@@ -0,0 +1,17 @@
+# $FreeBSD: src/sbin/mount_udf/Makefile,v 1.6 2006/07/17 20:53:25 stefanf Exp $
+
+PROG=	mount_udf2
+SRCS=	mount_udf.c getmntopts.c
+MAN=	mount_udf2.8
+LIBADD=	kiconv
+
+MOUNT=	${.CURDIR:H}/mount
+CFLAGS+= -I${MOUNT} -I${SRCTOP}/sys
+.PATH:	${MOUNT}
+WARNS?= 1
+
+# Needs to be dynamically linked for optional dlopen() access to
+# userland libiconv
+NO_SHARED?=	NO
+
+.include <bsd.prog.mk>
diff -ruPx *.orig ../src-org/sbin/mount_udf2/Makefile.depend sbin/mount_udf2/Makefile.depend
--- ../src-org/sbin/mount_udf2/Makefile.depend	1970-01-01 09:00:00.000000000 +0900
+++ sbin/mount_udf2/Makefile.depend	2018-10-05 09:56:25.603032000 +0900
@@ -0,0 +1,18 @@
+# $FreeBSD: head/sbin/mount_udf/Makefile.depend 325188 2017-10-31 00:07:04Z bdrewery $
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+	gnu/lib/csu \
+	include \
+	include/xlocale \
+	lib/${CSU_DIR} \
+	lib/libc \
+	lib/libcompiler_rt \
+	lib/libkiconv \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff -ruPx *.orig ../src-org/sbin/mount_udf2/mount_udf.c sbin/mount_udf2/mount_udf.c
--- ../src-org/sbin/mount_udf2/mount_udf.c	1970-01-01 09:00:00.000000000 +0900
+++ sbin/mount_udf2/mount_udf.c	2018-10-05 09:56:25.606344000 +0900
@@ -0,0 +1,388 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2002 Scott Long
+ * Copyright (c) 2012 Will DeVries
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com).  The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * 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.
+ *
+ * $FreeBSD: src/sbin/mount_udf/mount_udf.c,v 1.13 2005/06/10 09:51:43 delphij Exp $
+ */
+
+/*
+ * This is just a rip-off of mount_iso9660.c.  It's been vastly simplified
+ * because UDF doesn't take any options at this time.
+ */
+
+#include <sys/cdio.h>
+#include <sys/file.h>
+#include <sys/iconv.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+#include <sys/endian.h>
+#include <sys/ioctl.h>
+#include <sys/udfio.h>
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+	MOPT_STDOPTS,
+	MOPT_UPDATE,
+	MOPT_END
+};
+
+static void	get_session_info(char *dev, struct udf_session_info *usi, 
+   		    int session_num);
+static void	print_session_info(char *dev, int session_num);
+static int	set_charset(char *, const char *);
+static void	usage(void);
+static int	get_uid(char *u, uid_t *uid);
+static int	get_gid(char *g, gid_t *gid);
+static int	get_mode(char *m, mode_t *mode);
+
+int
+main(int argc, char **argv)
+{
+	struct udf_session_info usi;
+	struct iovec *iov;
+	struct passwd *nobody;
+	long session_num;
+	gid_t anon_gid, override_gid;
+	int iovlen, ch, mntflags, opts, sessioninfo;
+	uid_t anon_uid, override_uid;
+	mode_t mode, dirmode;
+	char cs_local[ICONV_CSNMAXLEN];
+	char *dev, *dir, *endp, mntpath[MAXPATHLEN];
+	uint8_t use_nobody_gid, use_nobody_uid;
+	uint8_t use_override_gid, use_override_uid;
+	uint8_t use_mode, use_dirmode;
+
+	cs_local[0] = '\0';
+	session_num = 0;
+	sessioninfo = 0;
+	use_nobody_uid = use_nobody_gid = 1;
+	use_override_uid = use_override_gid = 0;
+	use_mode = use_dirmode = 0;
+	iov = NULL;
+	iovlen = 0;
+	mntflags = opts = 0;
+
+	while ((ch = getopt(argc, argv, "C:G:g:M:m:o:ps:U:u:")) != -1)
+		switch (ch) {
+		case 'C':
+			set_charset(cs_local, optarg);
+			break;
+		case 'G':
+			if (get_gid(optarg, &anon_gid) == -1)
+				errx(EX_USAGE, "invalid gid in option G: %s", 
+				    optarg);
+			use_nobody_gid = 0;
+			break;
+		case 'g':
+			if (get_gid(optarg, &override_gid) == -1)
+				errx(EX_USAGE, "invalid gid in option g: %s", 
+				    optarg);
+			use_override_gid = 1;
+			break;
+		case 'M':
+			if (get_mode(optarg, &dirmode) == -1)
+				errx(EX_USAGE, "invalid mode in option M: %s", 
+				    optarg);
+			use_dirmode = 1;
+			break;
+		case 'm':
+			if (get_mode(optarg, &mode) == -1)
+				errx(EX_USAGE, "invalid mode in option m: %s", 
+				    optarg);
+			use_mode = 1;
+			break;
+		case 'o':
+			getmntopts(optarg, mopts, &mntflags, &opts);
+			break;
+		case 'p':
+			sessioninfo = 1;	
+			break;
+		case 's':
+			session_num = strtol(optarg, &endp, 10);
+			if (optarg == endp || *endp != '\0')
+				errx(EX_USAGE, "invalid number in option s: %s", 
+				    optarg);
+			break;
+		case 'U':
+			if (get_uid(optarg, &anon_uid) == -1)
+				errx(EX_USAGE, "invalid uid in option U: %s", 
+				    optarg);
+			use_nobody_uid = 0;
+			break;
+		case 'u':
+			if (get_uid(optarg, &override_uid) == -1)
+				errx(EX_USAGE, "invalid uid in option u: %s", 
+				    optarg);
+			use_override_uid = 1;
+			break;
+		case '?':
+		default:
+			usage();
+		}
+	argc -= optind;
+	argv += optind;
+
+	if (sessioninfo == 1) {
+		if (argc != 1)
+			usage();
+		print_session_info(argv[0], session_num);
+	}
+
+	if (argc != 2)
+		usage();
+
+	dev = argv[0];
+	dir = argv[1];
+
+	/*
+	 * Resolve the mountpoint with realpath(3) and remove unnecessary
+	 * slashes from the devicename if there are any.
+	 */
+	(void)checkpath(dir, mntpath);
+	(void)rmslashes(dev, dev);
+
+	/*
+	 * Get session info from device
+	 */
+	get_session_info(dev, &usi, session_num);
+
+
+	/*
+	 * Use nobody for uid and gid if not given above. 
+	 */
+	if (use_nobody_gid == 1 && use_override_gid == 0) {
+		if (get_gid("nobody", &anon_gid) == -1)
+			errx(EX_USAGE, "There is no group 'nobody'; use the G "
+			    "option to specify a default gid.");
+	} else if (use_override_gid == 1)
+		anon_gid = override_gid;
+
+	if (use_nobody_uid == 1 && use_override_uid == 0) {
+		if (get_uid("nobody", &anon_uid) == -1)
+			errx(EX_USAGE, "There is no user 'nobody'; use the U "
+			    "option to specify a default uid.");
+	} else if (use_override_uid == 1)
+		anon_uid = override_uid;
+
+	/* UDF file systems are not writeable. */
+	mntflags |= MNT_RDONLY;
+
+	build_iovec(&iov, &iovlen, "fstype", "udf2", (size_t) - 1);
+	build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t) - 1);
+	build_iovec(&iov, &iovlen, "from", dev, (size_t) - 1);
+	build_iovec(&iov, &iovlen, "uid", &anon_uid, sizeof(anon_uid));
+	if (use_override_uid == 1)
+		build_iovec(&iov, &iovlen, "override_uid", NULL, 0);
+	build_iovec(&iov, &iovlen, "gid", &anon_gid, sizeof(anon_gid));
+	if (use_override_gid == 1)
+		build_iovec(&iov, &iovlen, "override_gid", NULL, 0);
+	if (use_mode)
+		build_iovec(&iov, &iovlen, "mode", &mode, sizeof(mode_t));
+	if (use_dirmode)
+		build_iovec(&iov, &iovlen, "dirmode", &dirmode, sizeof(mode_t));
+
+	build_iovec(&iov, &iovlen, "first_trackblank", 
+	    &usi.session_first_track_blank, sizeof(uint8_t));
+	build_iovec(&iov, &iovlen, "session_start_addr",
+	    &usi.session_start_addr, sizeof(uint32_t));
+	build_iovec(&iov, &iovlen, "session_end_addr", &usi.session_end_addr, 
+	    sizeof(uint32_t));
+	build_iovec(&iov, &iovlen, "session_last_written",
+	    &usi.session_last_written, sizeof(uint32_t));
+
+	if (cs_local[0] != '\0') {
+		build_iovec(&iov, &iovlen, "cs_local", cs_local, (size_t) - 1);
+	}
+
+	if (nmount(iov, iovlen, mntflags) < 0)
+		err(1, "%s", dev);
+
+	free(iov);
+	exit(0);
+}
+
+static int
+get_uid(char *u, uid_t *uid)
+{
+	struct passwd *usr;
+	char *endp;
+
+	usr = getpwnam(u);
+	if (usr != NULL)
+		*uid = usr->pw_gid;
+	else {
+		*uid = strtoul(u, &endp, 10);
+
+		if (u == endp || *endp != '\0')
+			return (-1);	
+	}
+
+	return (0);
+}
+
+static int
+get_gid(char *g, gid_t *gid)
+{
+	struct group *grp;
+	char *endp;
+
+	grp = getgrnam(g);
+	if (grp != NULL)
+		*gid = grp->gr_gid;
+	else {
+		*gid = strtoul(g, &endp, 10);
+
+		if (g == endp || *endp != '\0')
+			return (-1);	
+	}
+
+	return (0);
+}
+
+static int
+get_mode(char *m, mode_t *mode) 
+{
+	char *endp;
+
+	*mode = strtoul(m, &endp, 8);	
+	if (m == endp || *endp != '\0')
+		return (-1);	
+	return (0);
+}
+
+static int
+set_charset(char *cs_local, const char *localcs)
+{
+	int error;
+
+	if (modfind("udf2_iconv") < 0)
+		if (kldload("udf2_iconv") < 0 || modfind("udf2_iconv") < 0) {
+			errx(EX_OSERR, "cannot find or load \"udf2_iconv\" "
+			    "kernel module");
+		}
+
+	strncpy(cs_local, localcs, ICONV_CSNMAXLEN);
+	error = kiconv_add_xlat16_cspairs(ENCODING_UNICODE, cs_local);
+	if (error != 0)
+		err(EX_OSERR, "udf2_iconv");
+
+	return (0);
+}
+
+static void
+get_session_info(char *dev, struct udf_session_info *usi, int session_num)
+{
+	int fd, error;
+	unsigned int out;
+
+	fd = open(dev, O_RDONLY, 0);
+	if (fd < 0)
+		err(1, "open");
+
+	bzero(usi, sizeof(struct udf_session_info));
+	usi->session_num = session_num;
+	error = ioctl(fd, UDFIOREADSESSIONINFO, usi);
+	if (error != 0) {
+		if (session_num != 0)
+			errx(EX_USAGE, "Cannot mount selected session.  This "
+			    "device does not properly support multi-sessions "
+			    "disc.");
+
+		/* Other fatal errors besides EIO and ENXIO may exist, but 
+		trying to mount an invalid device shouldn't result in anything 
+		to bad. */ 
+		if (errno == EIO)
+			errx(EX_IOERR, "Device not ready.");
+		else if (errno == ENXIO)
+			errx(EX_IOERR, "No media present.");
+		else
+			warnx("Warning, this device does not properly support "
+			    "multi-sessions disc.");
+
+		/* We populate the end address inside the kernel. */
+		usi->session_start_addr = 0;
+		usi->session_end_addr = 0;
+	}
+
+	close(fd);
+}
+
+static void
+print_session_info(char *dev, int session_num) 
+{
+	struct udf_session_info usi;
+
+	rmslashes(dev, dev);
+	get_session_info(dev, &usi, session_num);
+
+	printf("Number of Sessions: %u\n", usi.num_sessions);
+	printf("Number of Tracks: %u\n", usi.num_tracks);
+	printf("First Track Number: %u\n", usi.first_track);
+	printf("Sector Size: %u\n", usi.sector_size);
+	
+	printf("Session Number: %u\n", usi.session_num);
+	printf("Session Start Address: %u\n", usi.session_start_addr);
+	printf("Session End Address: %u\n", usi.session_end_addr);
+	printf("Last Written Address in Session: %u\n", usi.session_last_written);
+	printf("First Track Number of Session: %u\n", usi.session_first_track);
+	printf("Last Track of Session: %u\n", usi.session_last_track);
+
+	exit(0);
+}
+
+static void
+usage(void)
+{
+
+	(void)fprintf(stderr, "usage: mount_udf [-v] [-C charset] [-G gid] "
+	    "[-o options] [-s session] [-U uid] special node\n");
+	(void)fprintf(stderr, "usage: mount_udf [-p] [-s session] special\n");
+	exit(EX_USAGE);
+}
diff -ruPx *.orig ../src-org/sbin/mount_udf2/mount_udf2.8 sbin/mount_udf2/mount_udf2.8
--- ../src-org/sbin/mount_udf2/mount_udf2.8	1970-01-01 09:00:00.000000000 +0900
+++ sbin/mount_udf2/mount_udf2.8	2018-10-05 09:56:25.607737000 +0900
@@ -0,0 +1,128 @@
+.\" Copyright (c) 2002
+.\"     Scott Long <scottl@FreeBSD.org>
+.\"	Jeroen Ruigrok van der Werven <asmodai@wxs.nl>
+.\" 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 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.
+.\"
+.\" $FreeBSD: src/sbin/mount_udf/mount_udf.8,v 1.6 2005/02/10 09:19:31 ru Exp $
+.\"
+.Dd January 13, 2013
+.Dt MOUNT_UDF2 8
+.Os
+.Sh NAME
+.Nm mount_udf2
+.Nd mount a UDF file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar options
+.Op Fl C Ar charset
+.Op Fl G Ar gid
+.Op Fl g Ar gid
+.Op Fl M Ar permissions
+.Op Fl m Ar permissions
+.Op Fl s Ar session 
+.Op Fl U Ar uid
+.Op Fl u Ar uid
+.Ar special node
+.Nm
+.Op Fl p
+.Op Fl s Ar session 
+.Ar special
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches the UDF file system residing on the device
+.Pa special
+to the global file system namespace at the location indicated by
+.Ar node .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl C Ar charset
+Specify local
+.Ar charset
+to convert Unicode file names.  By default UTF-8 is used.
+.It Fl G Ar gid
+Set the group used for files and directories without defined groups to 
+.Ar gid .
+By default this is set to the 'nobody' group.  (UDF allows for files 
+and directories without groups.)
+.It Fl g Ar gid
+Set the group for all files and directories in the file system to 
+.Ar gid .
+If this option is specified, the
+.Fl G
+option has no effect.
+.It Fl M Ar permissions
+Set the mode used for all directories in the file system to 
+.Ar permissions .
+This value should be specified as an octal number according to the rules in 
+chmod(1).
+.It Fl m Ar permissions
+Set the mode used for all files in the file system to 
+.Ar permissions .
+This value should be specified as an octal number according to the rules in 
+chmod(1).
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.It Fl p
+Print information about sessions on cd.  This option may be used to determine
+the number of sessions on the disk.  Sessions are numbered starting with 1, and
+by default information will be displayed for the largest non-empty session.
+.It Fl s Ar session
+Specify the 
+.Ar session
+to mount or display information about.
+This must be a positive integer greater than zero.  Valid values may be 
+determined using the
+.Fl p
+option.
+.It Fl U Ar uid
+Set the user used for files and directories without defined owners to 
+.Ar uid .
+By default this is set to the user 'nobody'.  (UDF allows for files 
+and directories without owners.)
+.It Fl u Ar uid
+Set the owner for all files and directories in the file system to 
+.Ar uid .
+If this option is specified, the
+.Fl U
+option has no effect.
+
+.El
+.Sh SEE ALSO
+.Xr cdcontrol 1 ,
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
diff -ruPx *.orig ../src-org/sys/fs/udf2/ecma167-udf.h sys/fs/udf2/ecma167-udf.h
--- ../src-org/sys/fs/udf2/ecma167-udf.h	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/ecma167-udf.h	2018-10-05 09:56:25.622741000 +0900
@@ -0,0 +1,833 @@
+/*-
+ * 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 -ruPx *.orig ../src-org/sys/fs/udf2/udf.h sys/fs/udf2/udf.h
--- ../src-org/sys/fs/udf2/udf.h	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf.h	2018-10-05 09:56:25.624404000 +0900
@@ -0,0 +1,174 @@
+/*-
+ * 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 "udf_osta.h"
+
+/* 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
+
+/* Configuration values */
+#define UDF_VAT_ALLOC_LIMIT	104857600		/* picked at random */
+#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 */
+
+/* structure space */
+#define UDF_ANCHORS		4	/* 256, 512, N-256, N */
+#define UDF_PARTITIONS		4	/* overkill */
+#define UDF_PMAPS		5	/* overkill */
+#define UDF_MAX_ALLOC_EXTENTS	50	/* overkill */
+
+/* constants */
+#define UDF_MAX_NAMELEN		255	/* as per SPEC */
+#define UDF_TRAN_EXTERNAL	0
+#define UDF_TRAN_INTERN		1
+#define UDF_TRAN_ZERO		2
+
+/* RW content hint for allocation and other purposes */
+#define UDF_C_DSCR		 2	/* update sectornr and CRC */
+
+/* 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
+
+/* 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    */
+
+/* mounting options */
+#define UDFMNT_KICONV 		1
+#define UDFMNT_OVERRIDE_UID	2
+#define UDFMNT_OVERRIDE_GID	4
+#define UDFMNT_USE_MASK		8
+#define UDFMNT_USE_DIRMASK	16
+
+/* malloc pools */
+MALLOC_DECLARE(M_UDFTEMP);
+
+struct udf_node;
+
+struct udf_lvintq {
+	uint32_t		start;
+	uint32_t		end;
+	uint32_t		pos;
+	uint32_t		wpos;
+};
+
+struct udf_mount {
+	struct mount		*vfs_mountp;
+	struct vnode		*devvp;	
+	struct g_consumer	*geomcp;
+	uint32_t		 sector_size;
+	uint64_t		 flags;
+	uid_t			 anon_uid;
+	gid_t			 anon_gid;
+	void			*iconv_d2l;		/* disk to local */
+	mode_t			 mode;
+	mode_t			 dirmode;
+
+	/* Used in mounting */
+	uint32_t		 first_trackblank;
+	uint32_t 		 session_start;
+	uint32_t		 session_end;
+	uint32_t		 session_last_written;
+
+	/* format descriptors */
+	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      */
+
+	/* logical to physical translations */
+	int 			 vtop[UDF_PMAPS+1];	/* vpartnr trans     */
+	int			 vtop_tp[UDF_PMAPS+1];	/* type of trans     */
+
+	/* 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_table_alloc_len;
+	uint8_t			*vat_table;
+
+	/* sparable */
+	uint32_t		 sparable_packet_size;
+	struct udf_sparing_table *sparing_table;
+
+	/* meta */
+	struct udf_node 	*metadata_node;		/* system node       */
+};
+
+/*
+ * UDF node describing a file/directory.
+ *
+ * BUGALERT claim node_mutex before reading/writing to prevent inconsistencies !
+ */
+struct udf_node {
+	struct vnode		*vnode;			/* vnode associated  */
+	struct udf_mount	*ump;
+
+	ino_t			 hash_id;		/* should contain inode */
+	int			 diroff;		/* used in lookup */
+
+	/* 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 udf_fid {
+	u_short		len;		/* length of data in bytes */
+	u_short		padding;		/* force longword alignment */
+	ino_t		ino;
+};
+
+#endif /* !_FS_UDF_UDF_H_ */
diff -ruPx *.orig ../src-org/sys/fs/udf2/udf_allocation.c sys/fs/udf2/udf_allocation.c
--- ../src-org/sys/fs/udf2/udf_allocation.c	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf_allocation.c	2018-10-05 09:56:25.627396000 +0900
@@ -0,0 +1,442 @@
+/*-
+ * Copyright (c) 2012 Will DeVries
+ * 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>
+#include <sys/endian.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/limits.h>
+#include <sys/malloc.h>
+
+#include "ecma167-udf.h"
+#include "udf.h"
+#include "udf_subr.h"
+
+
+void
+udf_calc_freespace(struct udf_mount *ump, uint64_t *sizeblks, 
+    uint64_t *freeblks)
+{
+	struct logvol_int_desc *lvid;
+	int num_vpart, vpart;
+	uint32_t *pos1, *pos2;
+
+	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.
+	 */
+
+	num_vpart = le32toh(lvid->num_part);
+
+#if 0
+	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 {
+#endif
+		/* 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 (le32toh(*pos1) != (uint32_t) -1) {
+				*freeblks += le32toh(*pos1);
+				*sizeblks += le32toh(*pos2);
+			}
+		}
+#if 0
+	}
+#endif
+
+	if (*freeblks > UDF_DISC_SLACK)
+		*freeblks -= UDF_DISC_SLACK;
+	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 end_foffset, foffset;
+	int eof, error, flags, part, rel, slot;
+	uint32_t lb_num, lb_packet, lb_rel, lb_size, len;
+	uint32_t ext_offset, udf_rw32_lbmap;
+	uint16_t vpart;
+
+	KASSERT(ump && icb_loc && lb_numres,("ump && icb_loc && lb_numres"));
+
+	vpart = le16toh(icb_loc->loc.part_num);
+	lb_num = le32toh(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 > le32toh(pdesc->part_len))
+			return (EINVAL);
+		*lb_numres = lb_num + le32toh(pdesc->start_loc);
+
+		/* extent from here to the end of the partition */
+		*extres = le32toh(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 */
+		error = udf_vat_read(ump, (uint8_t *)&udf_rw32_lbmap, 4, 
+		    ump->vat_offset + lb_num * 4);
+		if (error != 0)
+			return (error);
+
+		lb_num = le32toh(udf_rw32_lbmap);
+
+		/* transform into its disc logical block */
+		if (lb_num > le32toh(pdesc->part_len))
+			return (EINVAL);
+		*lb_numres = lb_num + le32toh(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 < le16toh(ump->sparing_table->rt_l); rel++) {
+			sme = &ump->sparing_table->entries[rel];
+			if (lb_packet == le32toh(sme->org)) {
+				/* NOTE maps to absolute disc logical block! */
+				*lb_numres = le32toh(sme->map) + lb_rel;
+				*extres = ump->sparable_packet_size - lb_rel;
+				return (0);
+			}
+		}
+
+		/* transform into its disc logical block */
+		if (lb_num > le32toh(pdesc->part_len))
+			return (EINVAL);
+		*lb_numres = lb_num + le32toh(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 = le32toh(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);
+			if (eof) {
+				UDF_UNLOCK_NODE(ump->metadata_node, 0);
+				return (EINVAL);
+			}
+			len = le32toh(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 = (uint64_t) lb_num * lb_size - foffset;
+
+		/* process extent offset */
+		lb_num = le32toh(s_icb_loc.loc.lb_num);
+		vpart = le16toh(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)
+			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);
+}
+
+/* 
+ * This is a simplified version of the udf_translate_file_extent function. 
+ */
+int
+udf_bmap_translate(struct udf_node *udf_node, uint32_t block, 
+		   int *exttype, uint64_t *lsector, uint32_t *maxblks)
+{
+	struct udf_mount *ump;
+	struct icb_tag *icbtag;
+	struct long_ad s_ad, t_ad;
+	uint64_t foffset, new_foffset;
+	int addr_type, eof, error, flags, icbflags, slot;
+	uint32_t ext_offset, ext_remain, lb_num, lb_size, len, transsec32;
+	uint32_t translen;
+	uint16_t vpart_num;
+
+
+	if (udf_node == NULL)
+		return (ENOENT);
+#if 0
+	KASSERT(num_lb > 0,("num_lb > 0"));
+#endif
+	UDF_LOCK_NODE(udf_node, 0);
+
+	/* initialise derivative vars */
+	ump = udf_node->ump;
+	lb_size = le32toh(ump->logical_vol->lb_size);
+
+	if (udf_node->fe != NULL)
+		icbtag = &udf_node->fe->icbtag;
+	else
+		icbtag = &udf_node->efe->icbtag;
+
+	icbflags = le16toh(icbtag->flags);
+	addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+	/* do the work */
+	if (addr_type == UDF_ICB_INTERN_ALLOC) {
+		*exttype = UDF_TRAN_INTERN;
+		*maxblks = 1;
+		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);
+		if (eof) {
+			UDF_UNLOCK_NODE(udf_node, 0);
+			return (EINVAL);
+		}
+		len = le32toh(s_ad.len);
+		flags = UDF_EXT_FLAGS(len);
+		len = UDF_EXT_LEN(len);
+
+		if (flags == UDF_EXT_REDIRECT) {
+			slot++;
+			continue;
+		}
+
+		new_foffset = foffset + len;
+
+		if (new_foffset > (uint64_t) block * lb_size)
+			break;	/* found */
+		foffset = new_foffset;
+		slot++;
+	}
+	/* found overlapping slot */
+
+	lb_num = le32toh(s_ad.loc.lb_num);
+	vpart_num = le16toh(s_ad.loc.part_num);
+	
+	ext_offset = (uint64_t) block * lb_size - foffset;
+	lb_num += (ext_offset + lb_size - 1) / lb_size;
+	ext_remain = (len - ext_offset + lb_size - 1) / lb_size;
+
+	/*
+	 * note that the while(){} is nessisary for the extent that
+	 * the udf_translate_vtop() returns doens't have to span the
+	 * whole extent.
+	 */
+	switch (flags) {
+	case UDF_EXT_FREE:
+	case UDF_EXT_ALLOCATED_BUT_NOT_USED:
+		*exttype = UDF_TRAN_ZERO;
+		*maxblks = ext_remain;
+		break;
+	case UDF_EXT_ALLOCATED:
+		*exttype = UDF_TRAN_EXTERNAL;
+		t_ad.loc.lb_num = htole32(lb_num);
+		t_ad.loc.part_num = htole16(vpart_num);
+		error = udf_translate_vtop(ump, &t_ad, &transsec32, &translen);
+		if (error != 0) {
+			UDF_UNLOCK_NODE(udf_node, 0);
+			return (error);
+		}
+		*lsector = transsec32;
+		*maxblks = MIN(ext_remain, translen);
+		break;
+	default:
+		UDF_UNLOCK_NODE(udf_node, 0);
+		return (EINVAL);
+	}
+
+	UDF_UNLOCK_NODE(udf_node, 0);
+
+	return (0);
+}
+
+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;
+	int addr_type, adlen, extnr, icbflags;
+	uint32_t dscr_size, flags, l_ad, l_ea, offset;
+	uint8_t *data_pos;
+
+	/* determine what descriptor we are in */
+
+	fe = udf_node->fe;
+	efe = udf_node->efe;
+	if (fe != NULL) {
+		icbtag = &fe->icbtag;
+		dscr_size = sizeof(struct file_entry) -1;
+		l_ea = le32toh(fe->l_ea);
+		l_ad = le32toh(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 = le32toh(efe->l_ea);
+		l_ad = le32toh(efe->l_ad);
+		data_pos = (uint8_t *)efe + dscr_size + l_ea;
+	}
+
+	icbflags = le16toh(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,
+			    ("addr_type == UDF_ICB_LONG_ALLOC"));
+			long_ad = (struct long_ad *)(data_pos + l_ad-adlen);
+			l_icb = *long_ad;
+		}
+		flags = UDF_EXT_FLAGS(le32toh(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 = le32toh(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) {
+		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;
+	}
+}
diff -ruPx *.orig ../src-org/sys/fs/udf2/udf_filenames.c sys/fs/udf2/udf_filenames.c
--- ../src-org/sys/fs/udf2/udf_filenames.c	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf_filenames.c	2018-10-05 09:56:25.629423000 +0900
@@ -0,0 +1,276 @@
+/*-
+ * Copyright (c) 2012 Will DeVries
+ * 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/param.h>
+#include <sys/malloc.h>
+#include <sys/iconv.h>
+#include <sys/systm.h>
+
+#include "ecma167-udf.h"
+#include "udf.h"
+#include "udf_subr.h"
+
+extern struct iconv_functions *udf2_iconv;
+
+static int
+udf_to_utf8(char **result, size_t *rrem, uint32_t ch) 
+{
+	int n = 0;
+	char *rp = *result;
+
+	if ((ch & 0xFFFFFF80) == 0) {
+		if (*rrem < 1)
+			return (0);
+
+		n = 1;
+		rp[0] = ch & 0x7F;
+	} else if ((ch & 0xFFFFF800) == 0) {
+		if (*rrem < 2)
+			return (0);
+
+		n = 2;
+		rp[0] = 0xC0 | (ch >> 6);
+		rp[1] = 0x80 | (0x3F & ch);
+	} else if ((ch & 0xFFFF0000) == 0) {
+		if (*rrem < 3)
+			return (0);
+
+		n = 3;
+		rp[0] = 0xE0 | (ch >> 12);
+		rp[1] = 0x80 | (0x3F & (ch >> 6));
+		rp[2] = 0x80 | (0x3F & ch);
+	} else if ((ch & 0xFFE00000) == 0) {
+		if (*rrem < 4)
+			return (0);
+
+		n = 4;
+		rp[0] = 0xF0 | (ch >> 18);
+		rp[1] = 0x80 | (0x3F & (ch >> 12));
+		rp[2] = 0x80 | (0x3F & (ch >> 6));
+		rp[3] = 0x80 | (0x3F & ch);
+	} else {
+		/* do not convert points above 21 bits. */
+		return (0);
+	}
+
+	*rrem -= n;
+	*result += n;
+	return (n);
+}
+
+static void 
+udf_convert_str(struct udf_mount *ump, char *result, int *result_len,
+    uint8_t *id, int id_len, uint16_t *index, int *needsCRC, int *extloc, 
+    int eightbit) 
+{
+	size_t chrem, rrem;
+	int endi, i, invalid;
+	uint32_t uch;
+	char ch[2], *rp;
+	const char *chp;
+
+	if (eightbit)
+		endi = id_len;
+	else
+		endi = (id_len - 1 > 0) ? id_len - 1 : 0;
+
+	*extloc = 0;
+	*needsCRC = 0;
+	invalid = 0;
+	rp = result;
+	rrem = *result_len - 1; /* for the null */
+	for (i = 0; i < endi;) { 
+		if (eightbit) {
+			uch = id[i];
+			if (index)
+				index[i] = *result_len - 1 - rrem; 
+		} else {
+			uch = id[i] << 8 | id[i+1];
+			if (index)
+				index[i/2] = *result_len - 1 - rrem; 
+		}
+
+		//id[i] starts at this place in result
+
+		if (rrem == 0) {
+			/* no more space, we need to truncate it. */
+			*needsCRC = 1;	
+		} else if (uch == 0 || uch == 0x2F) {
+			/* do not allow nulls or slashes */
+			invalid++;
+		} else if (ump->flags & UDFMNT_KICONV && udf2_iconv) {
+			/* it might be a valid character */
+			chrem = 2;
+			chp = ch;
+			ch[0] = uch >> 8;
+			ch[1] = uch & 0x00FF;
+			udf2_iconv->convchr(ump->iconv_d2l, &chp, &chrem, &rp,
+			    &rrem);
+			if (chrem > 0) {
+				/* not printable or doesn't fit */
+				invalid++;
+				*needsCRC = 1;
+			} else
+				invalid = 0;
+		} else {
+			/* utf8 output */
+			/* it is a valid character */
+			if (udf_to_utf8(&rp, &rrem, uch) == 0) {
+				/* doesn't fit or too large */
+				invalid++;
+				*needsCRC = 1;
+			} else
+				invalid = 0;
+		}
+
+		if (uch == 0x002E && i != 1) {
+			/* record locations of periods where they occur within
+			5 char of the end, but not at the end or start */
+			if (eightbit && id_len - 6 < i && i + 1 != endi)
+				*extloc = i;
+			else if (!eightbit && id_len - 12 < i && i + 2 != endi)
+				*extloc = i;
+		}
+
+		if (rrem > 0 && invalid == 1) {
+			uch = 0x5F; // underscore
+
+			/* if the result doesn't have space this may not fit */
+			if (ump->flags & UDFMNT_KICONV && udf2_iconv) {
+				chrem = 2;
+				chp = ch;
+				ch[0] = uch >> 8;
+				ch[1] = uch & 0x00FF;
+				udf2_iconv->convchr(ump->iconv_d2l, &chp,
+				    &chrem, &rp, &rrem); 
+			} else
+				udf_to_utf8(&rp, &rrem, uch);
+
+			invalid++;
+		}
+
+		if (eightbit)
+			i++;
+		else
+			i += 2;
+	}
+
+	// this is the null placement
+	if (index) {
+		if (eightbit)
+			index[i] = *result_len - 1 - rrem; 
+		else
+			index[i/2] = *result_len - 1 - rrem; 
+	}
+
+	*rp = '\0';
+	*result_len -= rrem;
+}
+
+/*
+ * The result_len is assumed to include the zero.
+ * id_len - 1 is character, not \0.
+ */
+void
+udf_to_unix_name(struct udf_mount *ump, char *result, int result_len, 
+    uint8_t *id, int id_len)
+{
+	int crclen, eightbit, extlen, extloc, i, junkloc, mainlen, maxmainlen;
+	int maxnpart, needsCRC;
+	uint16_t crcsum, *index;
+	char *crc, crcbuf[6], *ext;
+
+	if (id[0] != 8 && id[0] != 16) {
+		/* this is either invalid or an empty string */
+		result_len = 0;
+		return;
+	} 
+
+	if (id[0] == 8)
+		eightbit = 1;
+	else 
+		eightbit = 0;
+	
+	/* remove marker byte from start of string */
+	id++;
+	id_len--;
+
+	index = malloc((id_len + 1) * sizeof(uint16_t), M_UDFTEMP, M_WAITOK);
+
+	mainlen = result_len;
+	udf_convert_str(ump, result, &mainlen, id, id_len, index, &needsCRC,
+	    &extloc, eightbit);
+
+	if (needsCRC) {
+		if (extloc) {
+			//build ext
+			ext = malloc(result_len, M_UDFTEMP, M_WAITOK | M_ZERO);
+			extlen = result_len;
+			udf_convert_str(ump, ext, &extlen, id + extloc, 
+			    id_len - extloc, NULL, &needsCRC, &junkloc,
+			    eightbit);
+		} else {
+			ext = NULL;
+			extlen = 1;
+		}
+		
+		crcsum = udf_cksum(id, id_len);
+		crcbuf[0] = '#';
+		crcbuf[1] = "0123456789ABCDEF"[(crcsum >> 12) & 0x000F];
+		crcbuf[2] = "0123456789ABCDEF"[(crcsum >> 8) & 0x000F];
+		crcbuf[3] = "0123456789ABCDEF"[(crcsum >> 4) & 0x000F];
+		crcbuf[4] = "0123456789ABCDEF"[crcsum & 0x000F];
+		crcbuf[5] = '\0';
+
+		crc = malloc(result_len, M_UDFTEMP, M_WAITOK | M_ZERO);
+		crclen = result_len;
+		udf_convert_str(ump, crc, &crclen, crcbuf, 5, NULL, &needsCRC,
+		    &junkloc, 1);
+
+		// find index of last character to be written into filename.
+		maxnpart = result_len - crclen - extlen - 3;
+		if (extloc)
+			maxmainlen = (eightbit) ? extloc : extloc / 2;
+		else 
+			maxmainlen = (eightbit) ? id_len : id_len / 2;
+
+		for (i = 0; i < maxmainlen; i++)
+			if (index[i + 1] - 1 > maxnpart)
+				break;
+		i--;
+
+		memcpy(result + index[i + 1], crc, crclen - 1);
+		if (ext)
+			memcpy(result + index[i + 1] + crclen - 1, ext, extlen);
+		result[index[i + 1] + crclen + extlen - 2] = '\0';
+
+		if (ext)
+			free(ext, M_UDFTEMP);
+		free(crc, M_UDFTEMP);
+	}
+
+	free(index, M_UDFTEMP);
+}
diff -ruPx *.orig ../src-org/sys/fs/udf2/udf_iconv.c sys/fs/udf2/udf_iconv.c
--- ../src-org/sys/fs/udf2/udf_iconv.c	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf_iconv.c	2018-10-05 09:56:25.630519000 +0900
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2003 Ryuichiro Imura
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/fs/udf/udf_iconv.c,v 1.1 2003/11/05 06:56:08 scottl Exp $");
+
+#include <sys/param.h>
+#include <sys/iconv.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/mount.h>
+
+VFS_DECLARE_ICONV(udf2);
diff -ruPx *.orig ../src-org/sys/fs/udf2/udf_osta.c sys/fs/udf2/udf_osta.c
--- ../src-org/sys/fs/udf2/udf_osta.c	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf_osta.c	2018-10-05 09:56:25.631905000 +0900
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ *
+ * Copyright to this code held by AT&T.
+ */
+
+#include "udf_osta.h"
+
+/*
+ * CRC 010041
+ */
+static uint16_t 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
+};
+
+uint16_t
+udf_cksum(unsigned char *s, int n)
+{
+	uint16_t crc = 0;
+
+	while (n-- > 0)
+		crc = crc_table[(crc >> 8 ^ *s++) & 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) {
+        int count;
+        uint16_t checksum = 0;
+
+        for (count = 0; count < 48; count++)
+               checksum += *data++;
+
+        return (checksum);
+}
+
+#ifdef MAIN
+unsigned char bytes[] = { 0x70, 0x6A, 0x77 };
+
+main(void)
+{
+	unsigned short x;
+
+	x = udf_cksum(bytes, sizeof bytes);
+	printf("checksum: calculated=%4.4x, correct=%4.4x\en", x, 0x3299);
+
+	exit(0);
+}
+#endif
+
+
diff -ruPx *.orig ../src-org/sys/fs/udf2/udf_osta.h sys/fs/udf2/udf_osta.h
--- ../src-org/sys/fs/udf2/udf_osta.h	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf_osta.h	2018-10-05 09:56:25.632537000 +0900
@@ -0,0 +1,14 @@
+/*
+ * Prototypes for the OSTA functions
+ */
+
+
+#ifndef _FS_UDF_OSTA_H_
+#define _FS_UDF_OSTA_H_
+
+#include <sys/types.h>
+
+unsigned short	udf_cksum(unsigned char *, int);
+uint16_t	udf_ea_cksum(uint8_t *data);
+
+#endif /* _FS_UDF_OSTA_H_ */
diff -ruPx *.orig ../src-org/sys/fs/udf2/udf_readwrite.c sys/fs/udf2/udf_readwrite.c
--- ../src-org/sys/fs/udf2/udf_readwrite.c	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf_readwrite.c	2018-10-05 09:56:25.634486000 +0900
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 2012 Will DeVries
+ * 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>
+#include <sys/endian.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+
+#include "ecma167-udf.h"
+#include "udf.h"
+#include "udf_subr.h"
+
+
+static int	udf_read_phys_sectors(struct udf_mount *ump, int what, 
+		    void *blob, uint32_t start, uint32_t sectors);
+
+/*
+ * 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().
+ */
+int
+udf_read_node(struct udf_node *unode, uint8_t *blob, off_t start, int length)
+{
+	struct vnode *devvp = unode->ump->devvp;
+	struct buf *bp;
+	uint64_t file_size, lsect;
+	int addr_type, exttype, error, icbflags;
+	uint32_t blkinsect, fileblk, fileblkoff, numb, numlsect, sector_size;
+	uint8_t  *pos;
+
+	error = 0;
+	sector_size = unode->ump->sector_size;
+	blkinsect = sector_size / DEV_BSIZE;
+
+	if (unode->fe != NULL) {
+		pos = &unode->fe->data[0] + le32toh(unode->fe->l_ea);
+		icbflags = le16toh(unode->fe->icbtag.flags);
+		file_size = le64toh(unode->fe->inf_len);
+	} else {
+		pos = &unode->efe->data[0] + le32toh(unode->efe->l_ea);
+		icbflags = le16toh(unode->efe->icbtag.flags);
+		file_size = le64toh(unode->efe->inf_len);
+	}
+
+	length = ulmin(file_size - start, length);
+	fileblk = start / sector_size;
+	fileblkoff = start % sector_size;
+
+	addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+	if (addr_type == UDF_ICB_INTERN_ALLOC) {
+		numb = ulmin(length, file_size - fileblkoff);
+		memcpy(blob, pos + fileblkoff, numb);
+		return (error);
+	}
+
+	while (length) {
+		error = udf_bmap_translate(unode, fileblk, &exttype, &lsect,
+		    &numlsect);
+		if (error != 0)
+			return (error);
+
+		if (exttype == UDF_TRAN_ZERO) {
+			numb = ulmin(length, (uint64_t) sector_size * numlsect - fileblkoff);
+			memset(blob, 0, numb);
+			length -= numb;
+			blob += numb;
+			fileblkoff = 0;
+		} else if (exttype == UDF_TRAN_INTERN)
+			return (EDOOFUS);
+		else {
+			while (numlsect > 0) {
+				error = bread(devvp, lsect * blkinsect,
+				    sector_size, NOCRED, &bp);
+				if (error != 0) {
+					if (bp != NULL)
+						brelse(bp);
+					return (error);
+				}
+		
+				numb = min(length, sector_size - fileblkoff);
+				bcopy(bp->b_data + fileblkoff, blob, numb);
+				brelse(bp);
+				bp = NULL;
+		
+				blob += numb;
+				length -= numb;
+				lsect++;
+				numlsect--;
+				fileblkoff = 0;
+			}
+		}
+		
+		fileblk += numlsect;
+	}
+
+	return (0);
+}
+
+/* SYNC reading of n blocks from specified sector */
+static int
+udf_read_phys_sectors(struct udf_mount *ump, int what, void *blob,
+    uint32_t start, uint32_t sectors)
+{
+	struct vnode *devvp = ump->devvp;
+	struct buf *bp;
+	int error = 0;
+	uint32_t blks, sector_size;
+
+	sector_size = ump->sector_size;
+	blks = sector_size / DEV_BSIZE;
+
+	while (sectors > 0 && error == 0) {
+		error = bread(devvp, start * blks, sector_size, NOCRED, &bp);
+		if (error != 0) {
+			if (bp != NULL)
+				brelse(bp);
+			return (error);
+		}
+
+		bcopy(bp->b_data, blob, sector_size);
+		brelse(bp);
+		bp = NULL;
+
+		blob = (void *)((uint8_t *)blob + sector_size);
+		start++;
+		sectors--;
+	}
+
+	return (0);
+}
+
+/* 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;
+	int dscrlen, error, i, sectors, sector_size;
+	uint8_t *pos;
+
+	sector_size = ump->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);
+
+	if (error == 0) {
+		/* check if its a valid tag */
+		error = udf_check_tag(dst);
+		if (error != 0) {
+			/* 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);
+	}
+
+	if (!error && (dscrlen > sector_size)) {
+		/*
+		 * 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;
+	
+		pos = (uint8_t *)dst + sector_size;
+		error = udf_read_phys_sectors(ump, UDF_C_DSCR, pos, sector + 1,
+		    sectors - 1);
+	}
+	if (error == 0)
+		error = udf_check_tag_payload(dst, dscrlen);
+	if (error && dst) {
+		free(dst, mtype);
+		dst = NULL;
+	}
+	*dstp = dst;
+
+	return (error);
+}
diff -ruPx *.orig ../src-org/sys/fs/udf2/udf_subr.c sys/fs/udf2/udf_subr.c
--- ../src-org/sys/fs/udf2/udf_subr.c	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf_subr.c	2018-10-05 09:56:25.646100000 +0900
@@ -0,0 +1,1910 @@
+/*-
+ * Copyright (c) 2012 Will DeVries
+ * 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>
+#include <sys/endian.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/iconv.h>
+
+#include "ecma167-udf.h"
+#include "udf.h"
+#include "udf_subr.h"
+
+#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data)
+
+static int	udf_leapyear(int year);
+
+/*
+ * 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 cnt, *pos, sum;
+
+	/* 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 = le16toh(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 != le16toh(tag->desc_crc)) {
+		/* bad payload CRC; this is a broken tag */
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
+#if 0
+void
+udf_validate_tag_sum(void *blob)
+{
+	struct desc_tag *tag = blob;
+	uint8_t cnt, *pos, sum;
+
+	/* 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;
+	uint16_t crc, crc_len;
+	uint8_t *btag = (uint8_t *)tag;
+
+	crc_len = le16toh(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 = htole16(crc);
+	}
+
+	/* calculate TAG header checksum */
+	udf_validate_tag_sum(blob);
+}
+#endif
+
+/*
+ * 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 elmsz, num_lb, size, tag_id;
+
+	tag_id = le16toh(dscr->tag.id);
+
+	switch (tag_id) {
+	case TAGID_LOGVOL:
+		size = sizeof(struct logvol_desc) - 1;
+		size += le32toh(dscr->lvd.mt_l);
+		break;
+	case TAGID_UNALLOC_SPACE:
+		elmsz = sizeof(struct extent_ad);
+		size = sizeof(struct unalloc_sp_desc) - elmsz;
+		size += le32toh(dscr->usd.alloc_desc_num) * elmsz;
+		break;
+	case TAGID_FID:
+		size = UDF_FID_SIZE + dscr->fid.l_fi + le16toh(dscr->fid.l_iu);
+		size = (size + 3) & ~3;
+		break;
+	case TAGID_LOGVOL_INTEGRITY:
+		size = sizeof(struct logvol_int_desc) - sizeof(uint32_t);
+		size += le32toh(dscr->lvid.l_iu);
+		size += (2 * le32toh(dscr->lvid.num_part) * sizeof(uint32_t));
+		break;
+	case TAGID_SPACE_BITMAP:
+		size = sizeof(struct space_bitmap_desc) - 1;
+		size += le32toh(dscr->sbd.num_bytes);
+		break;
+	case TAGID_SPARING_TABLE:
+		elmsz = sizeof(struct spare_map_entry);
+		size = sizeof(struct udf_sparing_table) - elmsz;
+		size += le16toh(dscr->spt.rt_l) * elmsz;
+		break;
+	case TAGID_FENTRY:
+		size = sizeof(struct file_entry);
+		size += le32toh(dscr->fe.l_ea) + le32toh(dscr->fe.l_ad)-1;
+		break;
+	case TAGID_EXTFENTRY:
+		size = sizeof(struct extfile_entry);
+		size += le32toh(dscr->efe.l_ea) + le32toh(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 (le16toh(fid->tag.id) != TAGID_FID)
+		panic("got udf_fidsize on non FID\n");
+
+	size = UDF_FID_SIZE + fid->l_fi + le16toh(fid->l_iu);
+	size = (size + 3) & ~3;
+
+	/* We know this value will fit in an int. */
+	return (size);
+}
+
+void
+udf_lock_node(struct udf_node *udf_node, int flag, char const *fname, 
+    const int lineno)
+{
+#if 0
+	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);
+#endif
+}
+
+void
+udf_unlock_node(struct udf_node *udf_node, int flag)
+{
+#if 0
+	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);
+#endif
+}
+
+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_UDFTEMP,
+	    (union dscrptr **)dst);
+	if (error == 0) {
+		/* blank terminator blocks are not allowed here */
+		if (*dst == NULL)
+			return (ENOENT);
+		if (le16toh((*dst)->tag.id) != TAGID_ANCHOR) {
+			error = ENOENT;
+			free(*dst, M_UDFTEMP);
+			*dst = NULL;
+		}
+	}
+
+	return (error);
+}
+
+int
+udf_read_anchors(struct udf_mount *ump)
+{
+	struct anchor_vdp **anchorsp;
+	int first_anchor, anch, error, ok;
+	uint32_t positions[4], session_end, session_start;
+
+	session_start = ump->session_start;
+	session_end = ump->session_end;
+
+	/* read anchors start+256, start+512, end-256, end */
+	positions[0] = session_start + 256;
+	positions[1] = session_end - 256;
+	positions[2] = session_end;
+	/* XXX shouldn't +512 be prefered above +256 for compat with Roxio CD */
+	positions[3] = session_start + 512; /* [UDF 2.60/6.11.2] */
+
+	ok = 0;
+	anchorsp = ump->anchors;
+	first_anchor = 0;
+	if (ump->first_trackblank)
+		first_anchor = 1;
+	for (anch = first_anchor; anch < 4; anch++) {
+		if (positions[anch] <= session_end) {
+			error = udf_read_anchor(ump, positions[anch], anchorsp);
+			if (error == 0) {
+				anchorsp++;
+				ok++;
+			}
+		}
+	}
+
+	return (ok);
+}
+
+/* we dont try to be smart; we just record the parts */
+#define UDF_UPDATE_DSCR(name, dscr) \
+	if (name) \
+		free(name, M_UDFTEMP); \
+	name = dscr;
+
+static int
+udf_process_vds_descriptor(struct udf_mount *ump, union dscrptr *dscr)
+{
+	struct part_desc *part;
+	uint16_t phys_part, raw_phys_part;
+
+	switch (le16toh(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 ((le16toh(dscr->pd.flags) & UDF_PART_FLAG_ALLOCATED) == 0) {
+			free(dscr, M_UDFTEMP);
+			break;
+		}
+
+		/*
+		 * BUGALERT: some rogue implementations use random physical
+		 * partition numbers to break other implementations so lookup
+		 * the number.
+		 */
+		raw_phys_part = le16toh(dscr->pd.part_num);
+		for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) {
+			part = ump->partitions[phys_part];
+			if (part == NULL)
+				break;
+			if (le16toh(part->part_num) == raw_phys_part)
+				break;
+		}
+		if (phys_part == UDF_PARTITIONS) {
+			free(dscr, M_UDFTEMP);
+			return (EINVAL);
+		}
+
+		UDF_UPDATE_DSCR(ump->partitions[phys_part], &dscr->pd);
+		break;
+	case TAGID_VOL:		/* volume space extender; rare	*/
+		free(dscr, M_UDFTEMP);
+		break;
+	default:
+		free(dscr, M_UDFTEMP);
+	}
+
+	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;
+	int error;
+	uint32_t dscr_size, sector_size;
+
+	sector_size = ump->sector_size;
+
+	/* loc is sectornr, len is in bytes */
+	error = EIO;
+	while (len > 0) {
+		error = udf_read_phys_dscr(ump, loc, M_UDFTEMP, &dscr);
+		if (error != 0) {
+			if (!dscr)
+				free(dscr, M_UDFTEMP);
+			return (error);
+		}
+
+		/* blank block is a terminator */
+		if (dscr == NULL)
+			return (0);
+
+		/* TERM descriptor is a terminator */
+		if (le16toh(dscr->tag.id) == TAGID_TERM) {
+			free(dscr, M_UDFTEMP);
+			return (0);
+		}
+
+		/* process all others */
+		dscr_size = udf_tagsize(dscr, sector_size);
+
+		/* dscr is assigned into ump */
+		error = udf_process_vds_descriptor(ump, dscr);
+		if (error != 0) 
+			break;
+
+		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;
+	int error;
+	uint32_t main_len, main_loc, reserve_len, reserve_loc;
+
+	/*
+	 * 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];
+
+	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 = le32toh(anchor->main_vds_ex.loc);
+	main_len = le32toh(anchor->main_vds_ex.len);
+
+	reserve_loc = le32toh(anchor->reserve_vds_ex.loc);
+	reserve_len = le32toh(anchor->reserve_vds_ex.len);
+
+	error = udf_read_vds_extent(ump, main_loc, main_len);
+	if (error != 0) {
+		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.
+ *
+ * Note: this may not return in the media is malformed.
+ */
+static int
+udf_retrieve_lvint(struct udf_mount *ump)
+{
+	union dscrptr *dscr;
+	struct logvol_int_desc *lvint;
+	int dscr_type, error;
+	uint32_t lbnum, lb_size, len;
+
+	lb_size = le32toh(ump->logical_vol->lb_size);
+	len = le32toh(ump->logical_vol->integrity_seq_loc.len);
+	lbnum = le32toh(ump->logical_vol->integrity_seq_loc.loc);
+
+	lvint = NULL;
+	dscr = NULL;
+	error = 0;
+	while (len > 0) {
+		/* read in our integrity descriptor */
+		error = udf_read_phys_dscr(ump, lbnum, M_UDFTEMP, &dscr);
+		if (error == 0) {
+			if (dscr == NULL)
+				break;		/* empty terminates */
+			dscr_type = le16toh(dscr->tag.id);
+			if (dscr_type == TAGID_TERM)
+				break;		/* clean terminator */
+			if (dscr_type != TAGID_LOGVOL_INTEGRITY) {
+				printf("UDF mount: Invalid logical volume "
+				    "integrity sequence entry found.\n");
+#if 0
+				/* fatal... corrupt disc */
+				error = ENOENT;
+#endif
+				break;
+			}
+			if (lvint)
+				free(lvint, M_UDFTEMP);
+			lvint = &dscr->lvid;
+			dscr = NULL;
+		} /* else hope for the best... maybe the next is ok */
+
+		/* proceed sequential */
+		lbnum += 1;
+		len -= lb_size;
+
+		/* are we linking to a new piece? */
+		if (dscr && lvint->next_extent.len) {
+			len = le32toh(lvint->next_extent.len);
+			lbnum = le32toh(lvint->next_extent.loc);
+		}
+	}
+
+	/* clean up the mess, esp. when there is an error */
+	if (dscr)
+		free(dscr, M_UDFTEMP);
+
+	if (error != 0 && lvint != NULL) {
+		free(lvint, M_UDFTEMP);
+		lvint = NULL;
+	} else if (lvint == NULL) {
+		printf("UDF mount: No logical volume integrity sequence entries"
+		    " found.\n");
+		error = ENOENT;
+	}
+
+	ump->logvol_integrity = lvint;
+
+	return (error);
+}
+
+/*
+ * Checks if ump's vds information is correct and complete
+ */
+int
+udf_process_vds(struct udf_mount *ump) {
+	/* struct udf_args *args = &ump->mount_args; */
+	union udf_pmap *mapping;
+	struct logvol_int_desc *lvint;
+	struct part_desc *part;
+	struct udf_logvol_info *lvinfo;
+	int error, log_part, phys_part, pmap_size, pmap_stype, pmap_type;
+	int len, n_meta, n_phys, n_spar, n_virt, raw_phys_part;
+	uint32_t n_pm;
+	char *domain_name, *map_name; /* bits[128]; */
+	const char *check_name;
+	uint8_t *pmap_pos;
+
+	/* 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 (le32toh(ump->logical_vol->lb_size) != ump->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("UDF mount: disc not OSTA UDF Compliant, aborting\n");
+		return (EINVAL);
+	}
+
+	/*
+	 * 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.
+	 */
+	error = udf_retrieve_lvint(ump);
+	if (error != 0)
+		return (EINVAL); // previously it always returned this on error.
+
+	/* process derived structures */
+	n_pm = le32toh(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.
+	 */
+	n_pm = le32toh(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 */
+	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 = le16toh(mapping->pm1.vol_seq_num); */
+			raw_phys_part = le16toh(mapping->pm1.part_num);
+			pmap_type = UDF_VTOP_TYPE_PHYS;
+			n_phys++;
+			break;
+		case 2: /* virtual/sparable/meta mapping */
+			map_name = mapping->pm2.part_id.id;
+			/* volseq = le16toh(mapping->pm2.vol_seq_num); */
+			raw_phys_part = le16toh(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++;
+				break;
+			}
+			check_name = "*UDF Sparable Partition";
+			if (strncmp(map_name, check_name, len) == 0) {
+				pmap_type = UDF_VTOP_TYPE_SPARABLE;
+				n_spar++;
+				break;
+			}
+			check_name = "*UDF Metadata Partition";
+			if (strncmp(map_name, check_name, len) == 0) {
+				pmap_type = UDF_VTOP_TYPE_META;
+				n_meta++;
+				break;
+			}
+			break;
+		default:
+			return (EINVAL);
+		}
+
+		/*
+		 * BUGALERT: some rogue implementations use random physical
+		 * partition numbers to break other implementations so lookup
+		 * the number.
+		 */
+		for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) {
+			part = ump->partitions[phys_part];
+			if (part == NULL)
+				continue;
+			if (le16toh(part->part_num) == raw_phys_part)
+				break;
+		}
+
+		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);
+
+	/* 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;
+
+	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);
+}
+
+/*
+ * 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? */
+		spos = (uint16_t *)implext->data;
+		if (le16toh(*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 = le16toh(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;
+	int error;
+	int32_t l_ea;
+	uint32_t a_l, offset, sector_size;
+	uint8_t *pos;
+
+	/* get mountpoint */
+	sector_size = node->ump->sector_size;
+
+	/* get information from fe/efe */
+	if (node->fe != NULL) {
+		l_ea = le32toh(node->fe->l_ea);
+		eahdr = (struct extattrhdr_desc *) node->fe->data;
+	} else {
+		l_ea = le32toh(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 != 0)
+		return (EINVAL);
+	if (le16toh(eahdr->tag.id) != TAGID_EXTATTR_HDR)
+		return (EINVAL);
+	error = udf_check_tag_payload(eahdr, sizeof(struct extattrhdr_desc));
+	if (error != 0)
+		return (EINVAL);
+
+	/* 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 = le32toh(eahdr->impl_attr_loc);
+		if (offset == UDF_IMPL_ATTR_LOC_NOT_PRESENT)
+			return (ENOENT);
+	}
+	if (sattr == 65536) {				/* [4/48.10.9] */
+		offset = le32toh(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);
+
+	/* find our extended attribute  */
+	l_ea -= offset;
+	pos = (uint8_t *)eahdr + offset;
+
+	while (l_ea >= sizeof(struct extattr_entry)) {
+		attrhdr = (struct extattr_entry *)pos;
+		implext = (struct impl_extattr_entry *)pos;
+
+		/* get complete attribute length and check for roque values */
+		a_l = le32toh(attrhdr->a_l);
+		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
+		 */
+
+		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 int 
+udf_update_lvid_from_vat_extattr(struct udf_node *vat_node)
+{
+	struct impl_extattr_entry *implext;
+	struct vatlvext_extattr_entry lvext;
+	struct udf_mount *ump;
+	struct udf_logvol_info *lvinfo;
+	uint64_t vat_uniqueid;
+	int error;
+	uint32_t a_l, offset;
+	const char *extstr = "*UDF VAT LVExtension";
+	uint8_t *ea_start, *lvextpos;
+
+	/* get mountpoint and lvinfo */
+	ump = vat_node->ump;
+	lvinfo = ump->logvol_info;
+
+	/* get information from fe/efe */
+	if (vat_node->fe != NULL) {
+		vat_uniqueid = le64toh(vat_node->fe->unique_id);
+		ea_start = vat_node->fe->data;
+	} else {
+		vat_uniqueid = le64toh(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 !=0)
+		return (error);
+
+	implext = (struct impl_extattr_entry *)(ea_start + offset);
+	error = udf_impl_extattr_check(implext);
+	if (error != 0)
+		return (error);
+
+	/* paranoia */
+	if (a_l != sizeof(*implext) - 1 + le32toh(implext->iu_l) +
+	    sizeof(lvext))
+		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 (!!)
+	 */
+	lvextpos = implext->data + le32toh(implext->iu_l);
+	memcpy(&lvext, lvextpos, sizeof(lvext));
+
+	/* check if it was updated the last time */
+	if (le64toh(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 {
+		/* 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);
+}
+
+int
+udf_vat_read(struct udf_mount *ump, uint8_t *blob, int size, 
+    uint32_t offset)
+{
+/*	mutex_enter(&ump->allocate_mutex); */
+	if (offset + size > ump->vat_offset + ump->vat_entries * 4)
+		return (EINVAL);
+	memcpy(blob, ump->vat_table + offset, size);
+/*	mutex_exit(&ump->allocate_mutex); */
+
+	return (0);
+}
+
+/*
+ * 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;
+	int error, filetype;
+	uint32_t vat_entries, vat_length, vat_offset, vat_table_alloc_len;
+	uint32_t *raw_vat, sector_size;
+	char *regid_name;
+	uint8_t *vat_table;
+
+	/* vat_length is really 64 bits though impossible */
+
+	if (vat_node == NULL)
+		return (ENOENT);
+
+	/* get mount info */
+	ump = vat_node->ump;
+	sector_size = le32toh(ump->logical_vol->lb_size);
+
+	/* get information from fe/efe */
+	if (vat_node->fe != NULL) {
+		vat_length = le64toh(vat_node->fe->inf_len);
+		icbtag = &vat_node->fe->icbtag;
+		mtime = &vat_node->fe->mtime;
+		unique_id = le64toh(vat_node->fe->unique_id);
+	} else {
+		vat_length = le64toh(vat_node->efe->inf_len);
+		icbtag = &vat_node->efe->icbtag;
+		mtime = &vat_node->efe->mtime;
+		unique_id = le64toh(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);
+
+	vat_table_alloc_len =
+		((vat_length + UDF_VAT_CHUNKSIZE - 1) / UDF_VAT_CHUNKSIZE)
+			* UDF_VAT_CHUNKSIZE;
+
+	if (vat_table_alloc_len > UDF_VAT_ALLOC_LIMIT) {
+		printf("UDF mount: VAT table length of %d bytes exceeds "
+		    "implementation limit.\n", vat_table_alloc_len);
+		return (ENOMEM);
+	}
+	vat_table = malloc(vat_table_alloc_len, M_UDFTEMP, M_WAITOK); 
+
+	/* allocate piece to read in head or tail of VAT file */
+	raw_vat = malloc(sector_size, M_UDFTEMP, 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 = udf_read_node(vat_node, (uint8_t *)raw_vat, 
+		    vat_entries * 4, sizeof(struct udf_oldvat_tail));
+		if (error != 0)
+			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 != 0) {
+			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 = udf_read_node(vat_node, (uint8_t *)raw_vat, 0, 
+		    sizeof(struct udf_vat));
+		if (error != 0)
+			goto out;
+
+		/* definition */
+		vat = (struct udf_vat *)raw_vat;
+		vat_offset = vat->header_len;
+		vat_entries = (vat_length - vat_offset) / 4;
+
+		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 = udf_read_node(vat_node, vat_table, 0, vat_length);
+	if (error != 0)
+		printf("UDF mount: Error reading in of complete VAT file."
+		    " (error %d)\n", error);
+	if (error != 0)
+		goto out;
+
+	ump->logvol_integrity->lvint_next_unique_id = htole64(unique_id);
+	ump->logvol_integrity->integrity_type = htole32(UDF_INTEGRITY_CLOSED);
+	ump->logvol_integrity->time = *mtime;
+
+	ump->vat_table_alloc_len = vat_table_alloc_len;
+	ump->vat_table = vat_table;
+	ump->vat_offset = vat_offset;
+	ump->vat_entries = vat_entries;
+
+out:
+	if (error != 0) {
+		if (vat_table != NULL)
+			free(vat_table, M_UDFTEMP);
+	}
+	free(raw_vat, M_UDFTEMP);
+
+	return (error);
+}
+
+static int
+udf_search_vat(struct udf_mount *ump)
+{
+	union dscrptr *dscr;
+	struct long_ad icb_loc;
+	struct udf_node *vat_node;
+	int error;
+	uint32_t early_vat_loc, vat_loc;
+	uint16_t tagid;
+	uint8_t file_type;
+
+	vat_node = NULL;
+
+	early_vat_loc = ump->first_possible_vat_location;
+	vat_loc = ump->last_possible_vat_location;
+
+	/* start looking from the end of the range */
+	do {
+		error = udf_read_phys_dscr(ump, vat_loc, M_UDFTEMP, &dscr);
+		if (!error && dscr) { /* dscr will be null if zeros were read */
+			tagid = le16toh(dscr->tag.id);
+			file_type = 0;
+			if (tagid == TAGID_FENTRY)
+			    file_type = dscr->fe.icbtag.file_type;
+			else if (tagid == TAGID_EXTFENTRY)
+			    file_type = dscr->efe.icbtag.file_type; 
+			free(dscr, M_UDFTEMP);
+
+			if (file_type == 248)
+			{
+				icb_loc.loc.part_num = 
+				    htole16(UDF_VTOP_RAWPART);
+				icb_loc.loc.lb_num = htole32(vat_loc);
+				error = udf_get_node(ump, icb_loc, &vat_node);
+				if (error == 0)
+					error = udf_check_for_vat(vat_node);
+				if (error == 0) {
+					udf_dispose_node(vat_node);
+					break;
+				}
+			}
+		}
+
+		if (vat_node != NULL) {
+			udf_dispose_node(vat_node);
+			vat_node = NULL;
+		}
+
+		if (vat_loc == ump->last_possible_vat_location)
+			printf("UDF mount: VAT not found at last written "
+			    "location\n");
+
+		vat_loc--;
+	} while (vat_loc >= early_vat_loc);
+
+	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;
+	int error, spar;
+	uint32_t lb_num;
+
+	/*
+	 * The partition mapping passed on to us specifies the information we
+	 * need to locate and initialise the sparable partition mapping
+	 * information we need.
+	 */
+
+	ump->sparable_packet_size = le16toh(pms->packet_len);
+
+	for (spar = 0; spar < pms->n_st; spar++) {
+		lb_num = pms->st_loc[spar];
+		error = udf_read_phys_dscr(ump, lb_num, M_UDFTEMP, &dscr);
+		if (!error && dscr) {
+			if (le16toh(dscr->tag.id) == TAGID_SPARING_TABLE) {
+				if (ump->sparing_table)
+					free(ump->sparing_table, M_UDFTEMP);
+				ump->sparing_table = &dscr->spt;
+				dscr = NULL;
+				break;	/* we're done */
+			}
+		}
+		if (dscr)
+			free(dscr, M_UDFTEMP);
+	}
+
+	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;
+	int error = 0;
+	char *windows_id = "*Microsoft Windows";
+
+	/* 
+	 * The mappings come from the logical volume descripor, and Windows does
+	 * not write a usable partion number into the metadata map descriptor. 
+	 */
+	if (strncmp(windows_id, ump->logical_vol->imp_id.id, 23) == 0)
+		icb_loc.loc.part_num = 0;
+	else
+		icb_loc.loc.part_num = pmm->part_num;
+
+	icb_loc.loc.lb_num = pmm->meta_file_lbn;
+	udf_get_node(ump, icb_loc, &ump->metadata_node);
+
+	if (ump->metadata_node == NULL) {
+		icb_loc.loc.lb_num = pmm->meta_mirror_file_lbn;
+		if (icb_loc.loc.lb_num != -1)
+			udf_get_node(ump, icb_loc, &ump->metadata_node);
+		
+		if (ump->metadata_node != NULL)
+			printf("UDF mount: Metadata file not readable, "
+			    "substituting Metadata copy file\n");
+	}
+
+	/* if we're mounting read-only we relax the requirements */
+	if (ump->metadata_node == NULL)
+		error = EFAULT;
+	
+	return (error);
+}
+
+int
+udf_read_vds_tables(struct udf_mount *ump)
+{
+	union udf_pmap *mapping;
+	int pmap_size, error;
+	uint32_t log_part, n_pm;
+	uint8_t *pmap_pos;
+
+	error = 0;
+
+	/* Iterate (again) over the part mappings for locations   */
+	n_pm = le32toh(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);
+			if (error != 0)
+				return (ENOENT);
+			break;
+		case UDF_VTOP_TYPE_SPARABLE:
+			/* load one of the sparable tables */
+			error = udf_read_sparables(ump, mapping);
+			if (error != 0)
+				return (ENOENT);
+			break;
+		case UDF_VTOP_TYPE_META:
+			/* load the associated file descriptors */
+			error = udf_read_metadata_nodes(ump, mapping);
+			if (error != 0)
+				return (ENOENT);
+			break;
+		default:
+			break;
+		}
+		pmap_size = pmap_pos[1];
+		pmap_pos += pmap_size;
+	}
+
+	return (0);
+}
+
+int
+udf_read_rootdirs(struct udf_mount *ump)
+{
+	union dscrptr *dscr;
+	struct mount *mp;
+	struct vnode *rootdir_node, *streamdir_node;
+	struct long_ad *dir_loc, fsd_loc;
+	ino_t ino;
+	int dscr_type, error;
+	uint32_t dummy, fsd_len, lb_num;
+
+	mp = ump->vfs_mountp;
+
+	/* TODO implement FSD reading in separate function like integrity? */
+	/* get fileset descriptor sequence */
+	fsd_loc = ump->logical_vol->lv_fsd_loc;
+	fsd_len = le32toh(fsd_loc.len);
+
+	dscr = NULL;
+	error = 0;
+	while (fsd_len > 0 || error != 0) {
+		/* translate fsd_loc to lb_num */
+		error = udf_translate_vtop(ump, &fsd_loc, &lb_num, &dummy);
+		if (error != 0)
+			break;
+		error = udf_read_phys_dscr(ump, lb_num, M_UDFTEMP, &dscr);
+		/* end markers */
+		if (error != 0 || dscr == NULL)
+			break;
+
+		/* analyse */
+		dscr_type = le16toh(dscr->tag.id);
+		if (dscr_type == TAGID_TERM)
+			break;
+		if (dscr_type != TAGID_FSD) {
+			free(dscr, M_UDFTEMP);
+			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 != NULL) {
+			free(ump->fileset_desc, M_UDFTEMP);
+		}
+		ump->fileset_desc = &dscr->fsd;
+		dscr = NULL;
+
+		/* continue to the next fsd */
+		fsd_len -= ump->sector_size;
+		fsd_loc.loc.lb_num = htole32(le32toh(fsd_loc.loc.lb_num) + 1);
+
+		/* follow up to fsd->next_ex (long_ad) if its not null */
+		if (le32toh(ump->fileset_desc->next_ex.len)) {
+			fsd_loc = ump->fileset_desc->next_ex;
+			fsd_len = le32toh(ump->fileset_desc->next_ex.len);
+		}
+	}
+	if (dscr != NULL)
+		free(dscr, M_UDFTEMP);
+
+	/* there has to be one */
+	if (ump->fileset_desc == NULL)
+		return (ENOENT);
+
+	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_id(*dir_loc, &ino);
+	if (error == 0)
+		error = udf_vget(mp, ino, LK_EXCLUSIVE, &rootdir_node);
+	if (error != 0)
+		return (ENOENT);
+
+	/*
+	 * Try the system stream directory; not very likely in the ones we
+	 * test, but for completeness.
+	 */
+	dir_loc = &ump->fileset_desc->streamdir_icb;
+	if (le32toh(dir_loc->len)) {
+		error = udf_get_node_id(*dir_loc, &ino);
+		if (error == 0)
+			error = udf_vget(mp, ino, LK_EXCLUSIVE, 
+			    &streamdir_node);
+		if (error != 0)
+			printf("UDF mount: streamdir defined but error in "
+			    "streamdir reading\n");
+#if 0
+		else {
+			printf("UDF mount: streamdir defined but ignored\n");
+			/*
+			 * TODO process streamdir `baddies' i.e. files we dont
+			 * want if R/W
+			 */
+		}
+#endif
+	}
+
+	/* release the vnodes again; they'll be auto-recycled later */
+	if (streamdir_node != NULL) {
+		/* This is not used later. */
+		vgone(streamdir_node);
+		vput(streamdir_node);
+	}
+	if (rootdir_node != NULL) {
+		/* Vnodes are not initialized correctly until mounting is
+		complete. */
+		vgone(rootdir_node);
+		vput(rootdir_node);
+	}
+
+	return (0);
+}
+
+/* 
+ * To make absolutely sure we are NOT returning zero, add one.  This can fail,
+ * but in final version should probably never fail.
+ */
+int
+udf_get_node_id(const struct long_ad icbptr, ino_t *ino)
+{
+	uint32_t blkn;
+	uint16_t part;
+
+	/* Just for now, this should be done in another way. */
+	blkn = le32toh(icbptr.loc.lb_num);
+	part = le16toh(icbptr.loc.part_num);
+
+	if ((blkn + 1) & 0xE0000000) {
+		printf("UDF: Block number too large to convert to inode "
+		    "number.\n");
+		return EDOOFUS;
+	}
+	if (part & 0xFFF8) {
+		printf("UDF: Partition number too large to convert to inode "
+		    "number.\n");
+		return EDOOFUS;
+	}
+
+	*ino = (blkn + 1) | (part << 29);
+
+	return (0);
+}
+
+void
+udf_get_node_longad(const ino_t ino, struct long_ad *icbptr)
+{
+	uint32_t blkn, ino2;
+	uint16_t part;
+
+	/* Just for now, this should be done in another way. */
+	ino2 = ino;
+	blkn = (ino2 & 0x1FFFFFFF) - 1;
+	part = (ino2 & 0xE0000000) >> 29;
+
+	icbptr->loc.lb_num = htole32(blkn);
+	icbptr->loc.part_num = htole16(part);
+}
+
+/* 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
+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);
+}
+
+/* These timestamp_to_timespec functions are done. */
+
+static int 
+udf_leapyear(int year)
+{
+	int i;
+
+	i = (year % 400 == 0) ? 1 : 0;
+	i |= (year % 100 == 0) ? 0 : 1; 
+	i &= (year % 4 == 0) ? 1 : 0;
+
+	return (i);
+}
+
+void
+udf_timestamp_to_timespec(struct udf_mount *ump,
+			  struct timestamp *timestamp,
+			  struct timespec  *timespec)
+{
+	time_t secs;
+	const int days_to_mon[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243,
+	    273, 304, 334};
+	uint32_t nsecs, usecs;
+	uint16_t tz, year;
+
+	year = le16toh(timestamp->year);
+	if (year < 1970 || timestamp->month < 1 || timestamp->month > 12) {
+		timespec->tv_sec = 0;
+		timespec->tv_nsec = 0;
+		return;
+	}
+
+	secs = timestamp->second;
+	secs += timestamp->minute * 60;
+	secs += timestamp->hour * 3600;
+	secs += (timestamp->day - 1) * 3600 * 24;
+	secs += days_to_mon[timestamp->month - 1] * 3600 * 24;
+
+	secs += (year - 1970) * 365 * 3600 * 24;
+	secs += ((year - 1 - 1968) / 4) * 3600 * 24;
+
+	if (year > 2100) {
+		secs -= (((year - 1 - 2100) / 100) + 1) * 3600 * 24;
+	}
+	if (year > 2400) {
+		secs += (((year - 1 - 2400) / 400) + 1) * 3600 * 24;
+	}
+	if (timestamp->month > 2) {
+		secs += (time_t)udf_leapyear(year) * 3600 * 24;
+	}
+
+	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  = le16toh(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 (le16toh(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;
+}
+
+/*
+ * 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 ftype, icbftype, mode, udf_perm;
+	uint16_t icbflags;
+
+	UDF_LOCK_NODE(udf_node, 0);
+
+	if (fe != NULL) {
+		udf_perm = le32toh(fe->perm);
+		icbftype = fe->icbtag.file_type;
+		icbflags = le16toh(fe->icbtag.flags);
+	} else {
+		/*assert(udf_node->efe != NULL); */
+		udf_perm = le32toh(efe->perm);
+		icbftype = efe->icbtag.file_type;
+		icbflags = le16toh(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);
+}
+
+/*
+ * 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 icb_loc,
+    struct udf_node **ppunode)
+{
+	union dscrptr *dscr;
+	struct udf_node *udf_node;
+	int dscr_type, eof, error, slot, strat, strat4096;
+	uint32_t dummy, lb_size, sector;
+
+	/* garbage check: translate udf_node_icb_loc to sectornr */
+	error = udf_translate_vtop(ump, &icb_loc, &sector, &dummy);
+	if (error != 0)
+		return (EINVAL);
+
+	/* initialise crosslinks, note location of fe/efe for hashing */
+	udf_node = udf_alloc_node();
+	udf_node->ump = ump;
+	udf_node->loc = icb_loc;
+/*	mutex_init(&udf_node->node_mutex, MUTEX_DEFAULT, IPL_NONE); */
+/*	cv_init(&udf_node->node_lock, "udf_nlk"); */
+
+	/* safe to unlock, the entry is in the hash table, vnode is locked */
+/*	mutex_exit(&ump->get_node_lock); */
+
+	strat4096 = 0;
+	lb_size = le32toh(ump->logical_vol->lb_size);
+
+	do {
+		/* try to read in fe/efe */
+		error = udf_translate_vtop(ump, &icb_loc, &sector, &dummy);
+		if (error == 0)
+			error = udf_read_phys_dscr(ump, sector, M_UDFTEMP, 
+			    &dscr);
+
+		/* blank sector marks end of sequence, check this */
+		if (dscr == NULL && strat4096 == 0)
+			error = ENOENT;
+
+		/* break if read error or blank sector */
+		if (error != 0 || dscr == NULL)
+			break;
+
+		/* process descriptor based on the descriptor type */
+		dscr_type = le16toh(dscr->tag.id);
+
+		/* if dealing with an indirect entry, follow the link */
+		if (dscr_type == TAGID_INDIRECTENTRY) {
+			free(dscr, M_UDFTEMP);
+			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)) {
+			free(dscr, M_UDFTEMP);
+			error = ENOENT;
+			break;
+		}
+
+		/* record and process/update (ext)fentry */
+		if (dscr_type == TAGID_FENTRY) {
+			if (udf_node->fe != NULL)
+				free(udf_node->fe, M_UDFTEMP);
+			udf_node->fe = &dscr->fe;
+			strat = le16toh(udf_node->fe->icbtag.strat_type);
+		} else {
+			if (udf_node->efe != NULL)
+				free(udf_node->efe, M_UDFTEMP);
+			udf_node->efe = &dscr->efe;
+			strat = le16toh(udf_node->efe->icbtag.strat_type);
+		}
+
+		/* 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;
+			icb_loc.loc.lb_num = le32toh(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 != 0) {
+				error = EINVAL;
+				break;
+			}
+			break;		/* done */
+		}
+	} while (!error);
+
+	/* first round of cleanup code */
+	if (error != 0) {
+		udf_dispose_node(udf_node);
+		return (EINVAL);
+	}
+
+	/*
+	 * 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);
+		if (eof != 0)
+			break;
+		slot++;
+
+		if (UDF_EXT_FLAGS(le32toh(icb_loc.len)) != UDF_EXT_REDIRECT)
+			continue;
+
+		if (udf_node->num_extensions >= UDF_MAX_ALLOC_EXTENTS) {
+			error = EINVAL;
+			break;
+		}
+
+		/* length can only be *one* lb : UDF 2.50/2.3.7.1 */
+		if (UDF_EXT_LEN(le32toh(icb_loc.len)) != lb_size) {
+			error = EINVAL;
+			break;
+		}
+
+		/* load in allocation extent */
+		error = udf_translate_vtop(ump, &icb_loc, &sector, &dummy);
+		if (error == 0)
+			error = udf_read_phys_dscr(ump, sector, M_UDFTEMP, 
+			    &dscr);
+		if (error != 0 || dscr == NULL)
+			break;
+
+		/* process read-in descriptor */
+		dscr_type = le16toh(dscr->tag.id);
+
+		if (dscr_type != TAGID_ALLOCEXTENT) {
+			free(dscr, M_UDFTEMP);
+			error = ENOENT;
+			break;
+		}
+
+		udf_node->ext[udf_node->num_extensions] = &dscr->aee;
+
+		udf_node->num_extensions++;
+
+	} /* while */
+	UDF_UNLOCK_NODE(udf_node, 0);
+
+	/* second round of cleanup code */
+	if (error != 0) {
+		/* recycle udf_node */
+		udf_dispose_node(udf_node);
+		return (EINVAL);		/* error code ok? */
+	}
+
+	/* TODO ext attr and streamdir udf_nodes */
+
+	*ppunode = udf_node;
+
+	return (0);
+}
+
+int
+udf_dispose_node(struct udf_node *udf_node)
+{
+	int extnr;
+
+	if (udf_node == NULL)
+		return (0);
+
+	/* TODO extended attributes and streamdir */
+
+	/* free associated memory and the node itself */
+	for (extnr = 0; extnr < udf_node->num_extensions; extnr++) {
+		free(udf_node->ext[extnr], M_UDFTEMP);
+		udf_node->ext[extnr] = (void *)0xdeadcccc;
+	}
+
+	if (udf_node->fe != NULL)
+		free(udf_node->fe, M_UDFTEMP);
+
+	if (udf_node->efe != NULL)
+		free(udf_node->efe, M_UDFTEMP);
+
+	udf_node->fe = (void *)0xdeadaaaa;
+	udf_node->efe = (void *)0xdeadbbbb;
+	udf_node->ump = (void *)0xdeadbeef;
+	udf_free_node(udf_node);
+
+	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_validate_fid(struct fileid_desc *fid, int *realsize)
+{
+	int error, fid_size;
+
+	/* check if our FID header is OK */
+	if ((error = udf_check_tag(fid)) != 0)
+		goto brokendir;
+
+	if (le16toh(fid->tag.id) != TAGID_FID) {
+		error = EIO;
+		goto brokendir;
+	}
+
+	/* check for length */
+	fid_size = udf_fidsize(fid);
+	if (*realsize < fid_size) {
+		error = EIO;
+		goto brokendir;
+	}
+
+	/* check FID contents */
+	error = udf_check_tag_payload((union dscrptr *)fid, *realsize);
+
+	*realsize = fid_size;
+brokendir:
+	if (error != 0)
+		return (EIO);
+
+	return (error);
+}
+
+/*
+ * 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
+ */
+
+int
+udf_read_internal(struct udf_node *node, uint8_t *blob)
+{
+	struct file_entry *fe = node->fe;
+	struct extfile_entry *efe = node->efe;
+	struct udf_mount *ump;
+	uint64_t inflen;
+	uint32_t sector_size;
+	uint8_t *pos;
+
+	/* get extent and do some paranoia checks */
+	ump = node->ump;
+	sector_size = ump->sector_size;
+
+	if (fe != NULL) {
+		inflen = le64toh(fe->inf_len);
+		pos = &fe->data[0] + le32toh(fe->l_ea);
+	} else {
+		inflen = le64toh(efe->inf_len);
+		pos = &efe->data[0] + le32toh(efe->l_ea);
+	}
+
+	/* copy out info */
+	memset(blob, 0, sector_size);
+	memcpy(blob, pos, inflen);
+
+	return (0);
+}
diff -ruPx *.orig ../src-org/sys/fs/udf2/udf_subr.h sys/fs/udf2/udf_subr.h
--- ../src-org/sys/fs/udf2/udf_subr.h	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf_subr.h	2018-10-05 09:56:25.647426000 +0900
@@ -0,0 +1,122 @@
+/*-
+ * 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)
+#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data)
+
+struct buf;
+struct long_ad;
+
+/* 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_dscr(struct udf_mount *ump, uint32_t sector,
+	    struct malloc_type *mtype, union dscrptr **dstp);
+
+/* 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);
+
+/* translation services */
+int	udf_translate_vtop(struct udf_mount *ump, struct long_ad *icb_loc,
+	    uint32_t *lb_numres, uint32_t *extres);
+int	udf_bmap_translate(struct udf_node *udf_node, uint32_t block, 
+	    int *exttype, uint64_t *lsector, uint32_t *maxblks);
+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_mount *ump, 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_calc_freespace(struct udf_mount *ump, uint64_t *sizeblks,
+	    uint64_t *freeblks);
+
+/* node readers and writers */
+#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 icb_loc,
+	    struct udf_node **ppunode);
+int	udf_dispose_node(struct udf_node *node);
+
+/* node ops */
+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);
+
+/* directory operations and helpers */
+void	udf_osta_charset(struct charspec *charspec);
+int	udf_validate_fid(struct fileid_desc *fid, int *realsize);
+int	udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen,
+	    struct long_ad *icb_loc, int *found);
+
+/* helpers and converters */
+int	udf_get_node_id(const struct long_ad icbptr, ino_t *ino);
+void	udf_get_node_longad(const ino_t ino, struct long_ad *icbptr);
+uint32_t udf_getaccessmode(struct udf_node *node);
+void	udf_to_unix_name(struct udf_mount *ump, char *result, int result_len,
+	    uint8_t *id, int len);
+void	udf_timestamp_to_timespec(struct udf_mount *ump,
+	    struct timestamp *timestamp, struct timespec *timespec);
+
+/* vnode operations */
+int	udf_getanode(struct mount *mp, struct vnode **vpp);
+int	udf_read_internal(struct udf_node *node, uint8_t *blob);
+
+/* Created by for testing */
+struct	udf_node * udf_alloc_node(void);
+void	udf_free_node(struct udf_node *unode);
+int	udf_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp);
+int	udf_read_node(struct udf_node *unode, uint8_t *blob, off_t start,
+	    int length);
+#endif	/* !_FS_UDF_UDF_SUBR_H_ */
diff -ruPx *.orig ../src-org/sys/fs/udf2/udf_vfsops.c sys/fs/udf2/udf_vfsops.c
--- ../src-org/sys/fs/udf2/udf_vfsops.c	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf_vfsops.c	2020-01-10 19:35:40.209173000 +0900
@@ -0,0 +1,763 @@
+/*-
+ * Copyright (c) 2012 Will DeVries
+ * 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/param.h>
+#include <sys/endian.h>
+#include <sys/cdefs.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/conf.h>
+#include <sys/module.h>
+#include <sys/priv.h>
+#include <sys/iconv.h>
+#include <sys/stat.h>
+#if 0
+#include <sys/udfio.h>
+#endif
+#include <geom/geom.h>
+#include <geom/geom_vfs.h>
+
+#include "ecma167-udf.h"
+#include "udf.h"
+#include "udf_subr.h"
+
+MALLOC_DEFINE(M_UDFTEMP, "UDF temp", "UDF allocation space");
+uma_zone_t udf_zone_node = NULL;
+
+struct iconv_functions *udf2_iconv = NULL;
+
+static int	udf_mountfs(struct vnode *, struct mount *); 
+
+
+/* predefine vnode-op list descriptor */
+
+static vfs_fhtovp_t	udf_fhtovp;
+static vfs_init_t	udf_init;
+static vfs_mount_t	udf_mount;
+static vfs_root_t	udf_root;
+static vfs_statfs_t	udf_statfs;
+static vfs_uninit_t	udf_uninit;
+static vfs_unmount_t	udf_unmount;
+
+static struct vfsops udf_vfsops = {
+	.vfs_init =		udf_init,
+	.vfs_uninit =		udf_uninit,
+	.vfs_mount =		udf_mount,
+	.vfs_root =		udf_root,
+	.vfs_statfs =		udf_statfs,
+	.vfs_unmount =		udf_unmount,
+	.vfs_fhtovp =		udf_fhtovp,
+	.vfs_vget =		udf_vget
+};
+VFS_SET(udf_vfsops, udf2, VFCF_READONLY);
+
+MODULE_VERSION(udf2, 1);
+
+
+static int
+udf_init(struct vfsconf *notused)
+{
+	/* init node pools */
+	udf_zone_node = uma_zcreate("UDF Node Pool Zone", 
+	    sizeof(struct udf_node), NULL, NULL, NULL, NULL, 0, 0);
+
+	if (udf_zone_node == NULL) {
+		printf("UDF mount: Cannot create node pool zone.");
+		return (ENOMEM);
+	}
+
+	return (0);
+}
+
+static int
+udf_uninit(struct vfsconf *notused)
+{
+	/* remove pools */
+	if (udf_zone_node != NULL) {
+		uma_zdestroy(udf_zone_node);
+		udf_zone_node = NULL;
+	}
+
+	return (0);
+}
+
+#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 == NULL)
+		return;
+
+	ump = VFSTOUDF(mp);
+	if (ump != NULL) {
+		/* Metadata partition support */
+		if (ump->metadata_node != NULL)
+			udf_dispose_node(ump->metadata_node);
+
+		/* clear our data */
+		for (i = 0; i < UDF_ANCHORS; i++)
+			MPFREE(ump->anchors[i], M_UDFTEMP);
+		MPFREE(ump->primary_vol, M_UDFTEMP);
+		MPFREE(ump->logical_vol, M_UDFTEMP);
+		MPFREE(ump->unallocated, M_UDFTEMP);
+		MPFREE(ump->implementation, M_UDFTEMP);
+		MPFREE(ump->logvol_integrity, M_UDFTEMP);
+		for (i = 0; i < UDF_PARTITIONS; i++) {
+			MPFREE(ump->partitions[i], M_UDFTEMP);
+		}
+		MPFREE(ump->fileset_desc, M_UDFTEMP);
+		MPFREE(ump->sparing_table, M_UDFTEMP);
+		MPFREE(ump->vat_table, M_UDFTEMP);
+
+		free(ump, M_UDFTEMP);
+	}
+}
+#undef MPFREE
+
+static int
+udf_mount(struct mount *mp)
+{
+	struct thread *td;	
+	struct vnode *devvp;
+	struct nameidata nd;
+	int error, len;
+	char *fspec;
+	
+	td = curthread;
+
+	MNT_ILOCK(mp);
+	mp->mnt_flag |= MNT_RDONLY;
+	MNT_IUNLOCK(mp);
+
+	if (mp->mnt_flag & MNT_ROOTFS)
+		return (ENOTSUP);
+
+	/* handle request for updating mount parameters */
+	/* TODO can't update my mountpoint yet */
+	if (mp->mnt_flag & MNT_UPDATE)
+		return (0);
+	
+	fspec = NULL;
+	error = vfs_getopt(mp->mnt_optnew, "from", (void **)&fspec, &len);
+	if (!error && fspec[len - 1] != '\0')
+		return (EINVAL);
+	if (fspec == NULL)
+		return (EINVAL);
+
+	/* lookup name to get its vnode */
+	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec);
+	if ((error = namei(&nd)))
+		return (error);
+	NDFREE_PNBUF(&nd);
+	devvp = nd.ni_vp;
+	if (vn_isdisk_error(devvp, &error) == 0) {
+		vput(devvp);
+		return (error);
+	}
+
+	error = VOP_ACCESS(devvp, VREAD, td->td_ucred, td);
+	if (error != 0)
+		error = priv_check(td, PRIV_VFS_MOUNT_PERM);
+	if (error != 0) {
+		vput(devvp);
+		return (error);
+	}
+
+	/*
+	 * Open device and try to mount it!
+	 */
+	error = udf_mountfs(devvp, mp);
+	if (error != 0) {
+		vrele(devvp);
+		return (error);
+	}
+
+	vfs_mountedfrom(mp, fspec);
+	return (0);
+}
+
+
+int
+udf_unmount(struct mount *mp, int mntflags)
+{
+	struct udf_mount *ump;
+	int error, flags;
+
+	ump = VFSTOUDF(mp);
+	if (ump == NULL)
+		panic("UDF unmount: empty ump\n");
+
+	flags = (mntflags & MNT_FORCE) ? FORCECLOSE : 0;
+
+	error = vflush(mp, 0, flags, curthread);
+	if (error != 0)
+		return (error);
+
+	if (ump->iconv_d2l != NULL)
+		udf2_iconv->close(ump->iconv_d2l);
+
+	g_topology_lock();
+	g_vfs_close(ump->geomcp);
+	g_topology_unlock();
+	vrele(ump->devvp);
+	dev_rel(ump->devvp->v_rdev);
+
+	/* free our ump */
+	free_udf_mountinfo(mp);
+
+	/* free ump struct references */
+	mp->mnt_data = NULL;
+	MNT_ILOCK(mp);
+	mp->mnt_flag &= ~MNT_LOCAL;
+	MNT_IUNLOCK(mp);
+
+	return (0);
+}
+
+#if 0
+static int
+get_session_info(struct g_consumer *cp, struct udf_session_info *usi, 
+    int session_num)
+{
+	int error;
+
+	bzero(usi, sizeof(struct udf_session_info));
+	usi->session_num = session_num;
+
+	error =  cp->provider->geom->ioctl(cp->provider, UDFIOREADSESSIONINFO,
+	    usi, 0, curthread);
+	if (error != 0) {
+		if (session_num != 0)
+			printf("Cannot mount selected session.  This "
+			    "device does not properly support multi-sessions "
+			    "disc.");
+		
+		printf("Warning, this device does not properly support "
+		    "multi-sessions disc.");
+
+		return (error);
+	}
+
+	printf("Number of Sessions: %u\n", usi->num_sessions);
+	printf("Number of Tracks: %u\n", usi->num_tracks);
+	printf("First Track Number: %u\n", usi->first_track);
+	printf("Sector Size: %u\n", usi->sector_size);
+	
+	printf("Session Number: %u\n", usi->session_num);
+	printf("Session Start Address: %u\n", usi->session_start_addr);
+	printf("Session End Address: %u\n", usi->session_end_addr);
+	printf("Last Written Address in Session: %u\n", usi->session_last_written);
+	printf("First Track Number of Session: %u\n", usi->session_first_track);
+	printf("Last Track of Session: %u\n", usi->session_last_track);
+
+	return (0);
+}
+#endif
+
+/*
+ * Helper function of udf_mount() that actually mounts the disc.
+ */
+static int
+udf_mountfs(struct vnode *devvp, struct mount *mp)
+{
+	struct g_consumer *cp;
+	struct udf_mount *ump = NULL;
+#if 0
+	struct udf_session_info usi;
+#endif
+	int error, len, num_anchors;
+	uint32_t bshift, logvol_integrity, numsecs; /*lb_size,*/
+	char *cs_local;
+	void *optdata = NULL;
+
+	/* Open a consumer. */
+	dev_ref(devvp->v_rdev);
+	g_topology_lock();
+	error = g_vfs_open(devvp, &cp, "udf2", 0);
+	g_topology_unlock();
+	VOP_UNLOCK(devvp);
+	if (error != 0)
+		goto fail;
+
+	/* setup basic mount information */
+	mp->mnt_stat.f_fsid.val[0] = dev2udev(devvp->v_rdev);
+	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
+	mp->mnt_stat.f_namemax = UDF_MAX_NAMELEN;
+	if (devvp->v_rdev->si_iosize_max != 0)
+		mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max;
+	if (mp->mnt_iosize_max > MAXPHYS)
+		mp->mnt_iosize_max = MAXPHYS;
+	MNT_ILOCK(mp);
+	mp->mnt_flag |= MNT_LOCAL;
+	mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED;
+	MNT_IUNLOCK(mp);
+
+	ump = malloc(sizeof(struct udf_mount), M_UDFTEMP, M_WAITOK | M_ZERO);
+
+#if 0
+	/* 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");
+#endif
+
+	/* set up linkage */
+	mp->mnt_data = ump;
+	ump->vfs_mountp = mp;
+	ump->devvp = devvp;
+	ump->geomcp = cp;
+
+	/* read in options */
+	error = vfs_getopt(mp->mnt_optnew, "uid", &optdata, &len);
+	if (error != 0 || len != sizeof(uid_t)) {
+		error = EINVAL;
+		goto fail;
+	}
+	ump->anon_uid = *(uid_t *)optdata;
+
+	vfs_flagopt(mp->mnt_optnew, "override_uid", &ump->flags,
+	    UDFMNT_OVERRIDE_UID); 
+
+	error = vfs_getopt(mp->mnt_optnew, "gid", &optdata, &len);
+	if (error != 0 || len != sizeof(gid_t)) {
+		error = EINVAL;
+		goto fail;
+	}
+	ump->anon_gid = *(gid_t *)optdata;
+
+	vfs_flagopt(mp->mnt_optnew, "override_gid", &ump->flags,
+	    UDFMNT_OVERRIDE_GID); 
+
+	if (vfs_getopt(mp->mnt_optnew, "mode", &optdata, &len) == 0) {
+		if (len != sizeof(mode_t)) {
+			error = EINVAL;
+			goto fail;
+		}
+		ump->mode = *(gid_t *)optdata & ALLPERMS;
+		ump->flags |= UDFMNT_USE_MASK;
+	}
+	
+	if (vfs_getopt(mp->mnt_optnew, "dirmode", &optdata, &len) == 0) {
+		if (len != sizeof(mode_t)) {
+			error = EINVAL;
+			goto fail;
+		}
+		ump->dirmode = *(gid_t *)optdata & ALLPERMS;
+		ump->flags |= UDFMNT_USE_DIRMASK;
+	}
+
+#if 0
+	printf("si_name: %s\n", devvp->v_rdev->si_name);
+	if (devvp->v_rdev->si_name[0] == 'c' && 
+	    devvp->v_rdev->si_name[1] == 'd')
+	{
+		error = get_session_info(cp, &usi, 0);
+		if (error != 0)
+			goto fail;
+
+		ump->first_trackblank = usi.session_first_track_blank;
+		ump->session_start = usi.session_start_addr;
+		ump->session_end = usi.session_end_addr;
+		ump->session_last_written = usi.session_last_written; 
+	} else {
+		ump->first_trackblank = 0;
+		ump->session_start = 0;
+		numsecs = cp->provider->mediasize / cp->provider->sectorsize;
+		ump->session_end = numsecs;
+		ump->session_last_written = numsecs;
+	}
+#endif
+
+	error = vfs_getopt(mp->mnt_optnew, "first_trackblank", &optdata, &len);
+	if (error != 0 || len != sizeof(uint8_t)) {
+		error = EINVAL;
+		goto fail;
+	}
+	ump->first_trackblank = *(uint8_t *)optdata;
+	
+	error = vfs_getopt(mp->mnt_optnew, "session_start_addr", &optdata,
+	    &len);
+	if (error != 0 || len != sizeof(uint32_t)) {
+		error = EINVAL;
+		goto fail;
+	}
+	ump->session_start = *(uint32_t *)optdata;
+
+	error = vfs_getopt(mp->mnt_optnew, "session_end_addr", &optdata, &len);
+	if (error != 0 || len != sizeof(uint32_t)) {
+		error = EINVAL;
+		goto fail;
+	}
+	ump->session_end = *(uint32_t *)optdata;
+
+	error = vfs_getopt(mp->mnt_optnew, "session_last_written", &optdata,
+	    &len);
+	if (error != 0 || len != sizeof(uint32_t)) {
+		error = EINVAL;
+		goto fail;
+	}
+	ump->session_last_written = *(uint32_t *)optdata;
+
+	/* We do not want the session_end value to be zero. */
+	numsecs = cp->provider->mediasize / cp->provider->sectorsize;
+	if (ump->session_end == 0)
+		ump->session_end = numsecs;
+	
+	/* We should only need to search one, so this is also a hack. */
+	if (ump->session_end - ump->session_start > 25)
+		ump->first_possible_vat_location = ump->session_last_written -
+		    25; 
+	else
+		ump->first_possible_vat_location = ump->session_start;
+	ump->last_possible_vat_location = ump->session_last_written;
+
+	error = vfs_getopt(mp->mnt_optnew, "cs_local", (void **)&cs_local,
+	    &len);
+	if (error == 0 && udf2_iconv != NULL) {
+		ump->flags |= UDFMNT_KICONV;
+
+		if (cs_local[len-1] != '\0') {
+			error = EINVAL;
+			goto fail;
+		}
+
+		udf2_iconv->open(cs_local, "UTF-16BE", &ump->iconv_d2l);
+	}
+
+	/* inspect sector size */
+	ump->sector_size = cp->provider->sectorsize;
+
+	bshift = 1;
+	while ((1 << bshift) < ump->sector_size)
+		bshift++;
+	if ((1 << bshift) != ump->sector_size) {
+		printf("UDF mount: hit implementation fence on sector size\n");
+		return (EIO);
+	}
+
+	/* temporary check to overcome sectorsize >= 8192 bytes panic */
+	if (ump->sector_size >= 8192) {
+		printf("UDF mount: hit implementation limit, sectorsize to "
+		    "big\n");
+		return (EIO);
+	}
+
+
+	/* read all anchors to get volume descriptor sequence */
+	num_anchors = udf_read_anchors(ump);
+	if (num_anchors == 0) {
+		printf("UDF mount: error reading anchors\n");
+		error = EINVAL;
+		goto fail;
+	}
+
+	/* read in volume descriptor sequence */
+	error = udf_read_vds_space(ump);
+	if (error != 0) {
+		printf("UDF mount: error reading volume space\n");
+		goto fail;
+	}
+
+	/* check consistency and completeness */
+	error = udf_process_vds(ump);
+	if (error != 0) {
+		printf( "UDF mount: disc not properly formatted (bad VDS)\n");
+		goto fail;
+	}
+
+	/* note that the mp info needs to be initialised for reading! */
+	/* read vds support tables like VAT, sparable etc. */
+	error = udf_read_vds_tables(ump);
+	if (error != 0) {
+		printf("UDF mount: error in format or damaged disc "
+		    "(VDS tables failing)\n");
+		goto fail;
+	}
+
+	/* check if volume integrity is closed otherwise its dirty */
+	logvol_integrity = le32toh(ump->logvol_integrity->integrity_type);
+	if (logvol_integrity != UDF_INTEGRITY_CLOSED) {
+		printf("UDF mount: file system was not cleanly unmounted.\n");
+#if 0
+		error = EPERM;
+		goto fail;
+#endif
+	}
+
+	/* read root directory */
+	error = udf_read_rootdirs(ump);
+	if (error != 0) {
+		printf("UDF mount: disc not properly formatted or damaged disc "
+		    "(rootdirs failing)\n");
+		goto fail;
+	}
+
+	/* success! */
+	return (0);
+
+fail:
+	if (cp != NULL) {
+		g_topology_lock();
+		g_vfs_close(cp);
+		g_topology_unlock();
+	}
+	dev_rel(devvp->v_rdev);
+	if (ump != NULL)
+		free_udf_mountinfo(mp);
+
+	return (error);
+}
+
+int
+udf_root(struct mount *mp, int flags, struct vnode **vpp)
+{
+	struct udf_mount *ump = VFSTOUDF(mp);
+	ino_t ino;
+	int error;
+
+	error = udf_get_node_id(ump->fileset_desc->rootdir_icb, &ino);
+	if (error == 0)
+		error = udf_vget(mp, ino, flags, vpp);
+	if (error != 0 && ((*vpp)->v_vflag & VV_ROOT) == 0) {
+		printf("UDF: Internal error: invalid root node.");
+		return (EDOOFUS);
+	}
+
+	return (error);
+}
+
+int
+udf_statfs(struct mount *mp, struct statfs *sbp)
+{
+	struct udf_mount *ump = VFSTOUDF(mp);
+	struct logvol_int_desc *lvid;
+	struct udf_logvol_info *impl;
+	uint64_t files, freeblks, sizeblks; 
+	int num_part;
+	
+	udf_calc_freespace(ump, &sizeblks, &freeblks);
+
+	files = 0;
+	lvid = ump->logvol_integrity;
+	num_part = le32toh(lvid->num_part);
+	impl = (struct udf_logvol_info *)(lvid->tables + 2 * num_part);
+	if (impl != NULL) {
+		files = le32toh(impl->num_files);
+		files += le32toh(impl->num_directories);
+	}
+	
+	sbp->f_version = STATFS_VERSION; 	/* structure version number */
+	/*uint32_t f_type;*/			/* type of filesystem */
+	sbp->f_flags = mp->mnt_flag; 		/* copy of mount exported flags */
+	sbp->f_bsize = ump->sector_size; 	/* filesystem fragment size */
+	sbp->f_iosize = ump->sector_size; 	/* optimal transfer block size */
+	sbp->f_blocks = sizeblks;		/* total data blocks in filesystem */
+	sbp->f_bfree  = freeblks;		/* free blocks in filesystem */
+	sbp->f_bavail = 0;			/* free blocks avail to non-superuser */
+	sbp->f_files = files;			/* total file nodes in filesystem */
+	sbp->f_ffree  = 0;			/* free nodes avail to non-superuser */
+	/*uint64_t f_syncwrites;*/		/* count of sync writes since mount */
+	/*uint64_t f_asyncwrites;*/		/* count of async writes since mount */
+	/*uint64_t f_syncreads;*/		/* count of sync reads since mount */
+	/*uint64_t f_asyncreads;*/		/* count of async reads since mount */
+	/*uint64_t f_spare[10];*/		/* unused spare */
+	/*uint32_t f_namemax;*/			/* maximum filename length */
+	/*uid_t	  f_owner;*/			/* user that mounted the filesystem */
+	/*fsid_t  f_fsid;*/			/* filesystem id */
+	/*char	  f_charspare[80];*/	    	/* spare string space */
+	/*char	  f_fstypename[MFSNAMELEN];*/ 	/* filesystem type name */
+	/*char	  f_mntfromname[MNAMELEN];*/  	/* mounted filesystem */
+	/*char	  f_mntonname[MNAMELEN];*/    	/* directory on which mounted */
+	
+	return (0);
+}
+
+struct udf_node *
+udf_alloc_node()
+{
+	return (uma_zalloc(udf_zone_node, M_WAITOK | M_ZERO));
+}
+
+void 
+udf_free_node(struct udf_node *unode)
+{
+	uma_zfree(udf_zone_node, unode);
+}
+
+/*
+ * 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, int flags, struct vnode **vpp)
+{
+	struct vnode *nvp;
+	struct udf_node *unode;
+	struct udf_mount *ump;
+	struct long_ad icb;
+	int error, udf_file_type;
+
+	error = vfs_hash_get(mp, ino, flags, curthread, vpp, NULL, NULL);
+	if (error != 0 || *vpp != NULL)
+		return (error);
+
+	if ((flags & LK_TYPE_MASK) == LK_SHARED) {
+		flags &= ~LK_TYPE_MASK;
+		flags |= LK_EXCLUSIVE;
+	}
+
+	ump = VFSTOUDF(mp);
+	error = udf_getanode(mp, &nvp);
+	if (error != 0)
+		return (error);
+	
+	lockmgr(nvp->v_vnlock, LK_EXCLUSIVE, NULL);
+	error = insmntque(nvp, mp);
+	if (error != 0)
+		return (error);
+
+	error = vfs_hash_insert(nvp, ino, flags, curthread, vpp, NULL, NULL);
+	if (error != 0 || *vpp != NULL)
+		return (error);
+
+	/* 
+	 * Load read and set up the unode structure.
+	 */
+	udf_get_node_longad(ino, &icb);
+	error = udf_get_node(ump, icb, &unode);
+	if (error != 0) {
+		vput(nvp);
+		return (error);
+	}
+	nvp->v_data = unode;
+	unode->vnode = nvp;
+	unode->hash_id = ino;
+
+	/* mark the root node as such */
+	if (ump->fileset_desc && 
+	    icb.loc.lb_num == ump->fileset_desc->rootdir_icb.loc.lb_num && 
+	    icb.loc.part_num == ump->fileset_desc->rootdir_icb.loc.part_num)
+		nvp->v_vflag |= VV_ROOT;
+
+	/*
+	 * 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.
+	 */
+	if (unode->fe != NULL)
+		udf_file_type = unode->fe->icbtag.file_type;
+	else
+		udf_file_type = unode->efe->icbtag.file_type;
+		
+	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;
+	}
+
+	if (nvp->v_type != VFIFO)
+		VN_LOCK_ASHARE(nvp);
+
+	*vpp = nvp;
+
+	return (0);
+}
+
+/*
+ * Lookup vnode for file handle specified
+ */
+int
+udf_fhtovp(struct mount *mp, struct fid *fhp, int flags,
+    struct vnode **vpp)
+{
+	struct vnode *vp;
+	struct udf_fid *ufid = (struct udf_fid*)fhp;
+	struct udf_node *udf_node;
+	uint64_t filelen;
+	int error;
+	
+	error = VFS_VGET(mp, ufid->ino, LK_EXCLUSIVE, &vp);
+	if (error != 0) {
+		*vpp = NULLVP;
+		return (error);
+	}
+
+	udf_node = VTOI(vp);
+	if (udf_node->efe != NULL)
+		filelen = le64toh(udf_node->efe->inf_len);
+	else
+		filelen = le64toh(udf_node->fe->inf_len);
+
+	vnode_create_vobject(vp, filelen, curthread);
+	*vpp = vp;
+
+	return (0);
+}
+
diff -ruPx *.orig ../src-org/sys/fs/udf2/udf_vnops.c sys/fs/udf2/udf_vnops.c
--- ../src-org/sys/fs/udf2/udf_vnops.c	1970-01-01 09:00:00.000000000 +0900
+++ sys/fs/udf2/udf_vnops.c	2021-12-04 10:14:30.604353000 +0900
@@ -0,0 +1,1159 @@
+/*-
+ * Copyright (c) 2013 Will DeVries
+ * 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/param.h>
+#include <sys/cdefs.h>
+#include <sys/endian.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/unistd.h>
+#include <sys/bio.h>
+#include <sys/stat.h>
+#include <sys/rwlock.h>
+#include <sys/sysctl.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/vm_object.h>
+#include <vm/vm_pager.h>
+#include <vm/vnode_pager.h>
+
+#include "ecma167-udf.h"
+#include "udf.h"
+#include "udf_subr.h"
+
+static vop_access_t	udf_access;
+static vop_bmap_t       udf_bmap;
+static vop_cachedlookup_t udf_cachedlookup;
+static vop_getattr_t	udf_getattr;
+static vop_ioctl_t      udf_ioctl;
+static vop_open_t	udf_open;
+static vop_pathconf_t   udf_pathconf;
+static vop_print_t      udf_print;
+static vop_read_t       udf_read;
+static vop_readdir_t    udf_readdir;
+static vop_readlink_t   udf_readlink;
+static vop_reclaim_t    udf_reclaim;
+static vop_setattr_t    udf_setattr;
+static vop_strategy_t   udf_strategy;
+static vop_vptofh_t     udf_vptofh;
+static vop_getpages_t	udf_getpages;
+
+static struct vop_vector udf_vnodeops = {
+	.vop_default =		&default_vnodeops,
+	.vop_access =		udf_access,
+	.vop_getattr =		udf_getattr,
+	.vop_open =		udf_open,
+	.vop_ioctl =		udf_ioctl,
+	.vop_pathconf =		udf_pathconf,
+	.vop_print =		udf_print,
+	.vop_read =		udf_read,
+	.vop_readdir =		udf_readdir,
+	.vop_readlink =		udf_readlink,
+	.vop_setattr =		udf_setattr,
+	.vop_strategy =		udf_strategy,
+	.vop_bmap =		udf_bmap,
+	.vop_cachedlookup =	udf_cachedlookup,
+	.vop_reclaim =		udf_reclaim,
+	.vop_vptofh =		udf_vptofh,
+	.vop_lookup =		vfs_cache_lookup,
+	.vop_getpages =		udf_getpages
+};
+VFS_VOP_VECTOR_REGISTER(udf_vnodeops);
+
+struct vop_vector udf_fifoops = {
+	.vop_access =	udf_access,
+	.vop_getattr =	udf_getattr,
+	.vop_print =	udf_print,
+	.vop_setattr =	udf_setattr,
+	.vop_reclaim =	udf_reclaim,
+	.vop_vptofh =	udf_vptofh,
+	.vop_default =	&fifo_specops,
+};
+VFS_VOP_VECTOR_REGISTER(udf_fifoops);
+
+/* implementations of vnode functions; table follows at end */
+
+
+int
+udf_getanode(struct mount *mp, struct vnode **vpp)
+{
+	return (getnewvnode("udf2", mp, &udf_vnodeops, vpp));
+}
+
+static int
+udf_reclaim(struct vop_reclaim_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct udf_node *udf_node = VTOI(vp);
+
+	if (udf_node == NULL)
+		return (0);
+
+	/* dispose all node knowledge */
+	vfs_hash_remove(vp);
+	udf_dispose_node(udf_node);
+	vp->v_data = NULL;
+
+	return (0);
+}
+
+static int
+udf_read(struct vop_read_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct uio *uio = ap->a_uio;
+	struct buf *bp;
+	struct udf_node *udf_node = VTOI(vp);
+	uint64_t fsize;
+	int seqcount, lbn, n, on, sector_size; 
+	int error = 0;
+	uint8_t *zerobuf;
+
+	/* can this happen? some filingsystems have this check */
+	if (uio->uio_offset < 0)
+		return (EINVAL);
+	if (uio->uio_resid == 0)
+		return (0);
+
+#ifdef INVARIANTS
+	/* As in ffs_read() */
+	if (vp->v_type != VDIR && vp->v_type != VREG)
+		panic("udf_read: type %d",  vp->v_type);
+#endif
+
+	/* get file/directory filesize */
+	if (udf_node->fe != NULL)
+		fsize = le64toh(udf_node->fe->inf_len);
+	else 
+		fsize = le64toh(udf_node->efe->inf_len);
+
+	sector_size = udf_node->ump->sector_size;
+
+	seqcount = ap->a_ioflag >> IO_SEQSHIFT;
+
+	while (error == 0 && uio->uio_resid > 0 && fsize > uio->uio_offset) {
+ 		lbn = uio->uio_offset / sector_size;
+		on = uio->uio_offset % sector_size;
+
+		n = min(sector_size - on, uio->uio_resid);
+		n = ulmin(n, fsize - uio->uio_offset);
+
+		if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0 &&
+		    (uint64_t) sector_size * (lbn + 1) < fsize) {
+			error = cluster_read(vp, fsize, lbn, sector_size, 
+			    NOCRED, on + uio->uio_resid, seqcount, 0, &bp);
+		} else {
+			error = bread(vp, lbn, sector_size, NOCRED, &bp);
+		}
+
+		n = min(n, sector_size - bp->b_resid);
+
+		if (error == 0) 
+			error = uiomove(bp->b_data + on, n, uio);
+
+		brelse(bp);
+	}
+
+	if (vp->v_type == VDIR && fsize <= uio->uio_offset &&
+	    uio->uio_resid > 0) {
+		zerobuf = malloc(2048, M_UDFTEMP, M_WAITOK | M_ZERO);
+		while (error == 0 && uio->uio_resid > 0) {
+			n = min(2048, uio->uio_resid);
+			error = uiomove(zerobuf, n, uio);
+		}
+		free(zerobuf, M_UDFTEMP);
+	}
+
+	return (error);
+}
+
+static int
+udf_bmap(struct vop_bmap_args /* {
+		struct vnode *a_vp;
+		daddr_t  a_bn;
+		struct bufobj **a_bop;
+		daddr_t *a_bnp;
+		int *a_runp;
+		int *a_runb;
+	} */ *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct udf_node *udf_node = VTOI(vp);
+	uint64_t lsector;
+	int error, exttype;
+	uint32_t maxblks;
+
+	if (ap->a_bop != NULL)
+		*ap->a_bop = &ap->a_vp->v_bufobj;
+
+	if (ap->a_bnp == NULL)
+		return (0);
+
+	/* get logical block and run */
+	error = udf_bmap_translate(udf_node, ap->a_bn, &exttype, &lsector,
+	    &maxblks);
+	if (error != 0)
+		return (error);
+
+	/* convert to dev blocks */
+	if (exttype == UDF_TRAN_INTERN)
+		*ap->a_bnp = INT64_MAX - 2;
+	else if (exttype == UDF_TRAN_ZERO)
+		*ap->a_bnp = INT64_MAX - 1; /* zero the buffer */
+	else
+		*ap->a_bnp = lsector * (udf_node->ump->sector_size/DEV_BSIZE);
+
+	/* set runlength of maximum block size */
+	if (ap->a_runp != NULL)
+		*ap->a_runp = maxblks - 1;
+
+	if (ap->a_runb != NULL) 
+		*ap->a_runb = 0;
+
+	/* return success */
+	return (0);
+}
+
+static int
+udf_strategy(struct vop_strategy_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct buf *bp = ap->a_bp;
+	struct udf_node *udf_node = VTOI(vp);
+	struct bufobj *bo = &udf_node->ump->devvp->v_bufobj;
+	uint64_t lsector;
+	int error, exttype;
+	uint32_t sector_size, maxblks;
+
+	if (vp->v_type == VBLK || vp->v_type == VCHR)
+		panic("udf_strategy: spec");
+
+	/* get sector size */
+	sector_size = udf_node->ump->sector_size;
+
+
+	/* get logical block and run */
+	if (bp->b_blkno == bp->b_lblkno) {
+		error = udf_bmap_translate(udf_node, bp->b_lblkno, &exttype,
+		    &lsector, &maxblks);
+
+		if (error != 0) {
+			bp->b_error = error;
+			bp->b_ioflags |= BIO_ERROR;
+			bufdone(bp);
+			return (error);
+		}
+
+		if (exttype == UDF_TRAN_ZERO) {
+			bp->b_blkno = INT64_MAX - 1;
+			vfs_bio_clrbuf(bp);
+		}
+		else if (exttype == UDF_TRAN_INTERN)
+			bp->b_blkno = INT64_MAX - 2;
+		else
+			bp->b_blkno = lsector * (sector_size / DEV_BSIZE);
+	}
+
+	if ((bp->b_iocmd & BIO_READ) == 0)
+		return (ENOTSUP);
+
+	if (bp->b_blkno == INT64_MAX - 1) {
+		bufdone(bp);
+//printf("UDF: Hole in file found. (This is a debuging statement, not an error.\n");
+	} else if (bp->b_blkno == INT64_MAX - 2) {
+		error = udf_read_internal(udf_node, (uint8_t *)bp->b_data);
+		if (error != 0) {
+			bp->b_error = error;
+			bp->b_ioflags |= BIO_ERROR;
+		}
+		bufdone(bp);
+	} else {
+		bp->b_iooffset = dbtob(bp->b_blkno);
+		BO_STRATEGY(bo, bp);
+	}
+
+	return (bp->b_error);
+}
+
+static int
+udf_readdir(struct vop_readdir_args *ap)
+{
+	struct uio *uio;
+	struct vnode *vp;
+	struct fileid_desc *fid;
+	struct dirent *dirent;
+	struct udf_mount *ump;
+	struct udf_node *udf_node;
+	uint64_t file_size;
+	u_long *cookies, *cookiesp;
+	off_t diroffset, transoffset;
+	int acookies, error, ncookies, size;
+	uint32_t lb_size;
+	uint8_t *fid_name;
+	
+	error = 0;
+	uio = ap->a_uio;
+	vp = ap->a_vp;
+	udf_node = VTOI(vp);
+	ump = udf_node->ump;
+	transoffset = uio->uio_offset;
+
+	/* This operation only makes sense on directory nodes. */
+	if (vp->v_type != VDIR)
+		return (ENOTDIR);
+
+	/* get directory filesize */
+	if (udf_node->fe != NULL)
+		file_size = le64toh(udf_node->fe->inf_len);
+	else
+		file_size = le64toh(udf_node->efe->inf_len);
+
+	dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK | M_ZERO);
+	if (ap->a_ncookies != NULL) {
+		/* is this the max number possible? */
+		ncookies = uio->uio_resid / 8;
+		if (ncookies > 1024)
+			ncookies = 1024;
+		cookies = malloc(sizeof(u_long) * ncookies, M_TEMP, 
+		    M_WAITOK | M_ZERO);
+		cookiesp = cookies;
+	} else {
+		ncookies = 0;
+		cookies = NULL;
+		cookiesp = NULL;
+	}
+	acookies = 0;
+
+	/* The directory '.' is not in the fid stream. */
+	if (transoffset == 0) {
+		memset(dirent, 0, sizeof(struct dirent));
+		dirent->d_fileno = udf_node->hash_id;
+		dirent->d_type = DT_DIR;
+		dirent->d_name[0] = '.';
+		dirent->d_namlen = 1;
+		dirent->d_reclen = GENERIC_DIRSIZ(dirent);
+		dirent_terminate(dirent);
+
+		if (uio->uio_resid >= dirent->d_reclen)
+		{
+			if (cookiesp != NULL) {
+				acookies++;
+				*cookiesp++ = 1; // next one
+			}
+			error = uiomove(dirent, dirent->d_reclen, uio);
+			if (error != 0)
+				goto bail;
+
+			transoffset = 1;
+		}
+	}
+
+	/* allocate temporary space for fid */
+	lb_size = ump->sector_size;
+	fid = malloc(lb_size, M_UDFTEMP, M_WAITOK);
+
+	/* we are called just as long as we keep on pushing data in */
+	if (transoffset == 1)
+		diroffset = 0;
+	else
+		diroffset = transoffset;
+
+	while (diroffset < file_size) {
+		/* transfer a new fid/dirent */
+		memset(fid, 0, lb_size);
+		size = MIN(file_size - diroffset, lb_size);
+
+		error = vn_rdwr(UIO_READ, vp, fid, size, diroffset, 
+		    UIO_SYSSPACE, IO_NODELOCKED, FSCRED, NULL, NULL, 
+		    curthread);
+		if (error != 0) {
+			printf("UDF: Error reading fid: %d\n", error);
+			break;
+		}
+
+		error = udf_validate_fid(fid, &size);
+		if (error != 0) {
+			printf("UDF: Invalid fid found: %d\n", error);
+			break;
+		}
+
+		diroffset += size;
+		
+		/* skip deleted and not visible files */
+		if (fid->file_char & UDF_FILE_CHAR_DEL ||
+		    fid->file_char & UDF_FILE_CHAR_VIS) {
+			transoffset = diroffset;
+			if (cookiesp != NULL && acookies > 0)
+				*(cookiesp - 1) = transoffset;
+			continue;
+		}
+
+		/* create resulting dirent structure */
+		memset(dirent, 0, sizeof(struct dirent));
+		error = udf_get_node_id(fid->icb, &dirent->d_fileno); /* inode hash XXX */
+		if (error != 0)
+			break;
+
+		/* Going for the filetypes now is too expensive. */
+		dirent->d_type = DT_UNKNOWN;
+		if (fid->file_char & UDF_FILE_CHAR_DIR)
+			dirent->d_type = DT_DIR;
+
+		/* '..' has no name, so provide one */
+		if (fid->file_char & UDF_FILE_CHAR_PAR) {
+			dirent->d_name[0] = '.';
+			dirent->d_name[1] = '.';
+			dirent->d_namlen = 2;
+		} else {
+			fid_name = fid->data + le16toh(fid->l_iu);
+			udf_to_unix_name(ump, dirent->d_name, MAXNAMLEN,
+			    fid_name, fid->l_fi);
+			dirent->d_namlen = strlen(dirent->d_name);
+		}
+
+		dirent->d_reclen = GENERIC_DIRSIZ(dirent);
+		dirent_terminate(dirent);
+
+		/* 
+		 * If there isn't enough space in the uio to return a
+		 * whole dirent, break off read
+		 */
+		if (uio->uio_resid < dirent->d_reclen)
+			break;
+
+		/* copy dirent to the caller */
+		if (cookiesp != NULL) {
+			if (acookies + 1 > ncookies)
+				break; 
+			acookies++;
+			*cookiesp++ = diroffset;
+		}
+
+		/* remember the last entry we transfered */
+		transoffset = diroffset;
+
+		error = uiomove(dirent, dirent->d_reclen, uio);
+		if (error != 0)
+			break;
+	}
+
+	/* pass on last transfered offset */
+	/* We lied for '.', so tell more lies. */
+	free(fid, M_UDFTEMP);
+
+	uio->uio_offset = transoffset; 
+
+bail:
+	if (ap->a_eofflag != NULL)
+		*ap->a_eofflag = uio->uio_offset >= file_size;
+
+	if (ap->a_ncookies != NULL) {
+		if (error != 0)
+			free(cookies, M_UDFTEMP);
+		else {
+			*ap->a_ncookies = acookies;
+			*ap->a_cookies = cookies;
+		}
+	}
+	free(dirent, M_UDFTEMP);
+
+	return (error);
+}
+
+static int
+udf_cachedlookup(struct vop_cachedlookup_args *ap)
+{
+	struct vnode *dvp = ap->a_dvp;
+	struct vnode **vpp = ap->a_vpp;
+	struct vnode *tdp = NULL;
+	struct componentname *cnp = ap->a_cnp;
+	struct fileid_desc *fid;
+	struct udf_node  *dir_node; 
+	struct udf_mount *ump;
+	uint64_t file_size, offset;
+	ino_t id = 0;
+	int error, islastcn, ltype, mounted_ro, nameiop, numpasses, unix_len;
+	int size;
+	uint8_t *fid_name;
+	char *unix_name;
+
+	dir_node = VTOI(dvp);
+	ump = dir_node->ump;
+	*vpp = NULL;
+	error = 0;
+
+	/* simplify/clarification flags */
+	nameiop = cnp->cn_nameiop;
+	islastcn = cnp->cn_flags & ISLASTCN;
+	mounted_ro = dvp->v_mount->mnt_flag & MNT_RDONLY;
+
+	/*
+	 * 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);
+
+	/* get directory filesize */
+	if (dir_node->fe != NULL)
+		file_size = le64toh(dir_node->fe->inf_len);
+	else
+		file_size = le64toh(dir_node->efe->inf_len);
+
+	if (nameiop != LOOKUP || dir_node->diroff == 0 || 
+	    dir_node->diroff > file_size) {
+		offset = 0;
+		numpasses = 1;
+	}
+	else {
+		offset = dir_node->diroff;
+		numpasses = 2;
+		nchstats.ncs_2passes++;
+	}
+
+	fid = malloc(ump->sector_size, M_UDFTEMP, M_WAITOK);
+	unix_name = malloc(MAXNAMLEN, M_UDFTEMP, M_WAITOK);
+lookuploop:
+	while (offset < file_size) {
+		/* transfer a new fid/dirent */
+		memset(fid, 0, ump->sector_size);
+		size = MIN(file_size - offset, ump->sector_size);
+
+		error = vn_rdwr(UIO_READ, dvp, fid, size, offset, UIO_SYSSPACE,
+		    IO_NODELOCKED, FSCRED, NULL, NULL, curthread);
+		if (error != 0) {
+			printf("UDF: Error reading fid: %d\n", error);
+			break;
+		}
+
+		error = udf_validate_fid(fid, &size);
+		if (error != 0) {
+			printf("UDF: Invalid fid found: %d\n", error);
+			break;
+		}
+
+		offset += size;
+
+		/* 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;
+		
+		if (fid->file_char & UDF_FILE_CHAR_PAR) {
+			if (cnp->cn_flags & ISDOTDOT) {
+				error = udf_get_node_id(fid->icb, &id);
+				break;
+			}
+		}
+		else {
+			fid_name = fid->data + le16toh(fid->l_iu);
+			udf_to_unix_name(ump, unix_name, MAXNAMLEN, fid_name,
+			    fid->l_fi);
+			unix_len = strlen(unix_name);
+
+			if (unix_len == cnp->cn_namelen) {
+				if (!strncmp(unix_name, cnp->cn_nameptr, 
+				    cnp->cn_namelen)) {
+					error = udf_get_node_id(fid->icb, &id);
+					break;
+				}
+			}
+		}
+	}
+
+	if (error != 0)
+		goto exit; 
+
+	if (id != 0) {
+		if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == LOOKUP)
+			dir_node->diroff = offset;
+		if (numpasses == 2)
+			nchstats.ncs_pass2++;
+
+		if (cnp->cn_flags & ISDOTDOT)
+			vn_vget_ino(dvp, id, cnp->cn_lkflags, &tdp);
+		else if (dir_node->hash_id == id) {
+			/* through a glass darkly... */
+			VREF(dvp);
+			ltype = cnp->cn_lkflags & LK_TYPE_MASK;
+			if (ltype != VOP_ISLOCKED(dvp)) {
+				if (ltype == LK_EXCLUSIVE)
+					vn_lock(dvp, LK_UPGRADE | LK_RETRY);
+				else
+					vn_lock(dvp, LK_DOWNGRADE | LK_RETRY);
+			}
+			tdp = dvp;
+		} else
+			error = udf_vget(ump->vfs_mountp, id, cnp->cn_lkflags,
+			    &tdp);
+
+		if (error == 0) {
+			*vpp = tdp;
+			if (cnp->cn_flags & MAKEENTRY) 
+				cache_enter(dvp, *vpp, cnp);
+		}
+	}
+	else {
+		if (numpasses-- == 2) {
+			offset = 0;
+			goto lookuploop;
+		}
+
+		if (cnp->cn_flags & MAKEENTRY)
+			cache_enter(dvp, *vpp, cnp);
+
+		if ((cnp->cn_flags & ISLASTCN) && 
+		    (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME))
+			error = EROFS;
+		else 
+			error = ENOENT;
+	}
+
+exit:
+	free(fid, M_UDFTEMP);
+	free(unix_name, M_UDFTEMP);
+
+	return (error);
+}
+
+static int
+udf_getattr(struct vop_getattr_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct udf_node *udf_node = VTOI(vp);
+	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, *attrtime, *creatime, *mtime;
+	struct udf_mount *ump = udf_node->ump;
+	uint64_t blkssize, filesize;
+	gid_t gid;
+	int error;
+	uid_t uid;
+	uint32_t a_l, nlink, offset;
+	uint8_t *filedata;
+
+	/* get descriptor information */
+	if (fe != NULL) {
+		nlink = le16toh(fe->link_cnt);
+		uid = (uid_t)le32toh(fe->uid);
+		gid = (gid_t)le32toh(fe->gid);
+		filesize = le64toh(fe->inf_len);
+		blkssize = le64toh(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 == 0) {
+			ft_extattr = (struct filetimes_extattr_entry *)
+				(filedata + offset);
+			if (ft_extattr->existence & UDF_FILETIMES_FILE_CREATION)
+				creatime = &ft_extattr->times[0];
+		}
+	} else {
+		nlink = le16toh(efe->link_cnt);
+		uid = (uid_t)le32toh(efe->uid);
+		gid = (gid_t)le32toh(efe->gid);
+		filesize = le64toh(efe->inf_len);	/* XXX or obj_size? */
+		blkssize = le64toh(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 || ump->flags & UDFMNT_OVERRIDE_UID)
+		uid = ump->anon_uid;
+	if (gid == (gid_t)-1 || ump->flags & UDFMNT_OVERRIDE_GID)
+		gid = ump->anon_gid;
+
+	/*
+	 * 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 (vp->v_type == VDIR) {
+		nlink++;
+
+		/* directories should be at least a single block? */
+		if (blkssize != 0) 
+			filesize = blkssize * ump->sector_size;
+		else
+			filesize = ump->sector_size;
+	}
+
+	/* 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);
+	if (vap->va_type == VDIR && ump->flags & UDFMNT_USE_DIRMASK)
+		vap->va_mode = (vap->va_mode & ~ALLPERMS) | ump->dirmode;
+	else if (vap->va_type != VDIR && ump->flags & UDFMNT_USE_MASK)
+		vap->va_mode = (vap->va_mode & ~ALLPERMS) | ump->mode;
+	vap->va_nlink = nlink;
+	vap->va_uid = uid;
+	vap->va_gid = gid;
+	vap->va_fsid = dev2udev(ump->devvp->v_rdev);
+	vap->va_fileid = udf_node->hash_id;
+	vap->va_size = filesize;
+	vap->va_blocksize = ump->sector_size; /* wise? */
+
+	/* 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;
+	vap->va_bytes = blkssize * ump->sector_size;
+	vap->va_filerev = 0; /* 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 != 0 || filedata == NULL)
+			vap->va_mode = 0;	/* or v_type = VNON?  */
+		else {
+			devattr = (struct device_extattr_entry *)filedata +
+			    offset;
+			vap->va_rdev = makedev(le32toh(devattr->major),
+			    le32toh(devattr->minor));
+			/* TODO we could check the implementator */
+		}
+	}
+
+	return (0);
+}
+
+static int
+udf_setattr(struct vop_setattr_args *ap)
+{
+	struct vattr *vap = ap->a_vap;
+
+	/* Abort if any unsettable attribute is given. */
+	if (vap->va_type != VNON ||
+	    vap->va_nlink != VNOVAL ||
+	    vap->va_fsid != VNOVAL ||
+	    vap->va_fileid != VNOVAL ||
+	    vap->va_blocksize != VNOVAL ||
+	    vap->va_gen != VNOVAL ||
+	    vap->va_rdev != VNOVAL ||
+	    vap->va_bytes != VNOVAL)
+		return (EINVAL);
+
+	if (vap->va_flags != VNOVAL ||
+	    vap->va_mode != (mode_t)VNOVAL || 
+	    vap->va_atime.tv_sec != VNOVAL ||
+	    vap->va_mtime.tv_sec != VNOVAL ||
+	    vap->va_uid != VNOVAL || 
+	    vap->va_gid != VNOVAL) {
+		return (EROFS);
+	}
+
+	if (vap->va_size != VNOVAL) {
+		if (vap->va_type == VDIR)
+			return (EISDIR);
+		if (vap->va_type == VLNK || vap->va_type == VREG)
+			return (EROFS);
+	}
+
+	return (0);
+}
+
+/*
+ * Return POSIX pathconf information for UDF file systems.
+ */
+static int
+udf_pathconf(struct vop_pathconf_args *ap)
+{
+	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 = NAME_MAX;
+		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 */
+		*ap->a_retval = 64; /* XXX ought to deliver 65 */
+		return (0);
+	}
+
+	return (EINVAL);
+}
+
+static int
+udf_open(struct vop_open_args *ap)
+{
+	struct udf_node *udf_node;
+	off_t file_size;
+
+	udf_node = VTOI(ap->a_vp);
+
+	if (udf_node->fe != NULL)
+		file_size = le64toh(udf_node->fe->inf_len);
+	else
+		file_size = le64toh(udf_node->efe->inf_len);
+
+	vnode_create_vobject(ap->a_vp, file_size, ap->a_td);
+
+	return (0);
+}
+
+static int
+udf_access(struct vop_access_args *ap)
+{
+	struct vnode *vp;
+	struct udf_node *udf_node;
+	struct udf_mount *ump;
+	accmode_t accmode;
+	gid_t gid;
+	mode_t mode;
+	uid_t uid;
+
+	vp = ap->a_vp;
+	udf_node = VTOI(vp);
+	accmode = ap->a_accmode;
+	ump = udf_node->ump;
+ 
+	/* check if we are allowed to write */
+	switch (vp->v_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 (accmode & VWRITE)
+			return (EROFS); /* check that this works */
+		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);
+	}
+
+	mode = udf_getaccessmode(udf_node);
+	if (vp->v_type == VDIR && ump->flags & UDFMNT_USE_DIRMASK)
+		mode = (mode & ~ALLPERMS) | ump->dirmode;
+	else if (vp->v_type != VDIR && ump->flags & UDFMNT_USE_MASK)
+		mode = (mode & ~ALLPERMS) | ump->mode;
+
+	if (udf_node->fe != NULL) {
+		uid = udf_node->fe->uid;
+		gid = udf_node->fe->gid;
+	}
+	else {
+		uid = udf_node->efe->uid;
+		gid = udf_node->efe->gid;
+	}
+
+	return (vaccess(vp->v_type, mode, uid, gid, accmode, ap->a_cred));
+}
+
+int
+udf_readlink(struct vop_readlink_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct uio *uio = ap->a_uio;
+	struct pathcomp pathcomp;
+	struct udf_node *udf_node;
+	int error, filelen, first, len, l_ci, mntonnamelen, namelen, pathlen;
+	int targetlen;
+	char *mntonname;
+	uint8_t *pathbuf, *pathpos, *targetbuf, *targetpos, *tmpname;
+
+	udf_node = VTOI(vp);
+
+	if (udf_node->efe != NULL)
+		filelen = le64toh(udf_node->efe->inf_len);
+	else
+		filelen = le64toh(udf_node->fe->inf_len);
+
+	if (UDF_SYMLINKBUFLEN - 1 < filelen)
+		return (EINVAL);
+
+	/* 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 + 1);
+
+	/* read contents of file in our temporary buffer */
+	error = vn_rdwr(UIO_READ, vp, pathbuf, filelen, 0, UIO_SYSSPACE,
+	    IO_NODELOCKED, FSCRED, NULL, NULL, curthread);
+	if (error != 0) {
+		/* 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 (filelen - 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 == 0) {
+				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 (filelen - 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(udf_node->ump, tmpname, MAXPATHLEN,
+			    pathcomp.ident, l_ci);
+			namelen = strlen(tmpname);
+			if (targetlen < namelen + 1) {
+				error = EINVAL;
+				break;
+			}
+			memcpy(targetpos, tmpname, namelen);
+			targetpos += namelen;
+			targetlen -= namelen;
+			if (filelen-pathlen > UDF_PATH_COMP_SIZE + l_ci) {
+				/* more follows, so must be directory */
+				*targetpos++ = '/';
+				targetlen--;
+			}
+			break;
+		default:
+			error = EINVAL;
+			break;
+		}
+		first = 0;
+		if (error != 0)
+			break;
+		pathpos += UDF_PATH_COMP_SIZE + l_ci;
+		pathlen += UDF_PATH_COMP_SIZE + l_ci;
+
+	}
+	/* all processed? */
+	if (filelen - pathlen > 0)
+		error = EINVAL;
+
+	/* uiomove() to destination */
+	if (error == 0)
+		uiomove(targetbuf, PATH_MAX - targetlen, uio);
+
+	free(pathbuf, M_UDFTEMP);
+	free(targetbuf, M_UDFTEMP);
+	free(tmpname, M_UDFTEMP);
+
+	return (error);
+}
+
+static int
+udf_ioctl(struct vop_ioctl_args *ap)
+{
+	return (ENOTTY);
+}
+
+static int
+udf_print(struct vop_print_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct udf_node *udf_node = VTOI(vp);
+
+	printf("    ino %u, on dev %s", (uint32_t)udf_node->hash_id,
+	    devtoname(udf_node->ump->devvp->v_rdev));
+	if (vp->v_type == VFIFO)
+		fifo_printinfo(vp);
+	printf("\n");
+
+	return (0);
+}
+
+static int
+udf_vptofh(struct vop_vptofh_args *ap)
+{
+	struct udf_node *udf_node = VTOI(ap->a_vp);
+	struct udf_fid *ufid = (struct udf_fid *)ap->a_fhp;
+
+	ufid->len = sizeof(struct udf_fid);
+	ufid->ino = udf_node->hash_id;
+
+	return (0);
+}
+
+SYSCTL_NODE(_vfs, OID_AUTO, udf2, CTLFLAG_RW, 0, "udf filesystem");
+static int use_buf_pager = 1;
+SYSCTL_INT(_vfs_udf2, OID_AUTO, use_buf_pager, CTLFLAG_RWTUN,
+	&use_buf_pager, 0,
+	"Use buffer pager instead of bmap");
+
+static daddr_t
+udf_gbp_getblkno(struct vnode *vp, vm_ooffset_t off)
+{
+	return (off / (VTOI(vp)->ump->sector_size));
+}
+
+static int
+udf_gbp_getblksz(struct vnode *vp, daddr_t lbn, long *sz)
+{
+	struct udf_node *udf_node = VTOI(vp);
+	uint64_t fsize;
+	int sector_size;
+
+	fsize = udf_node->fe
+		  ? le64toh(udf_node->fe->inf_len)
+		  : le64toh(udf_node->efe->inf_len);
+	sector_size = udf_node->ump->sector_size;
+	*sz = ((uint64_t)lbn * sector_size >= fsize ? 0 :
+		(uint64_t)(lbn + 1) * sector_size > fsize
+		  ? fsize - (uint64_t)lbn * sector_size : sector_size);
+	return (0);
+}
+
+static int
+udf_getpages(struct vop_getpages_args /* {
+		struct vnode *a_vp;
+		vm_page_t *a_m;
+		int a_count;
+		int a_reqpage;
+		vm_ooffset_t a_offset;
+	} */ *ap)
+{
+	if (use_buf_pager)
+		return (vfs_bio_getpages(ap->a_vp, ap->a_m, ap->a_count,
+			ap->a_rbehind, ap->a_rahead, udf_gbp_getblkno,
+			udf_gbp_getblksz));
+	return (vnode_pager_generic_getpages(ap->a_vp, ap->a_m, ap->a_count,
+		ap->a_rbehind, ap->a_rahead, NULL, NULL));
+}
diff -ruPx *.orig ../src-org/sys/kern/kern_sysctl.c sys/kern/kern_sysctl.c
--- ../src-org/sys/kern/kern_sysctl.c	2020-01-10 21:24:19.762554000 +0900
+++ sys/kern/kern_sysctl.c	2020-01-09 20:46:55.269575000 +0900
@@ -123,6 +123,26 @@
 static int	sysctl_old_kernel(struct sysctl_req *, const void *, size_t);
 static int	sysctl_new_kernel(struct sysctl_req *, void *, size_t);
 
+static void
+sysctl_lock(struct rm_priotracker *tracker)
+{
+
+	if (tracker != NULL)
+		SYSCTL_RLOCK(tracker);
+	else
+		SYSCTL_WLOCK();
+}
+
+static void
+sysctl_unlock(struct rm_priotracker *tracker)
+{
+
+	if (tracker != NULL)
+		SYSCTL_RUNLOCK(tracker);
+	else
+		SYSCTL_WUNLOCK();
+}
+
 static struct sysctl_oid *
 sysctl_find_oidname(const char *name, struct sysctl_oid_list *list)
 {
@@ -164,12 +184,8 @@
 
 	if (oid->oid_kind & CTLFLAG_DYN)
 		atomic_add_int(&oid->oid_running, 1);
+	sysctl_unlock(tracker);
 
-	if (tracker != NULL)
-		SYSCTL_RUNLOCK(tracker);
-	else
-		SYSCTL_WUNLOCK();
-
 	/*
 	 * Treat set CTLFLAG_NEEDGIANT and unset CTLFLAG_MPSAFE flags the same,
 	 * untill we're ready to remove all traces of Giant from sysctl(9).
@@ -178,11 +194,7 @@
 
 	KFAIL_POINT_ERROR(_debug_fail_point, sysctl_running, error);
 
-	if (tracker != NULL)
-		SYSCTL_RLOCK(tracker);
-	else
-		SYSCTL_WLOCK();
-
+	sysctl_lock(tracker);
 	if (oid->oid_kind & CTLFLAG_DYN) {
 		if (atomic_fetchadd_int(&oid->oid_running, -1) == 1 &&
 		    (oid->oid_kind & CTLFLAG_DYING) != 0)
diff -ruPx *.orig ../src-org/sys/modules/Makefile sys/modules/Makefile
--- ../src-org/sys/modules/Makefile	2020-01-10 21:24:27.771726000 +0900
+++ sys/modules/Makefile	2019-12-10 08:31:11.136958000 +0900
@@ -359,6 +359,8 @@
 	ubsec \
 	udf \
 	udf_iconv \
+	udf2 \
+	udf2_iconv \
 	ufs \
 	uinput \
 	unionfs \
diff -ruPx *.orig ../src-org/sys/modules/udf2/Makefile sys/modules/udf2/Makefile
--- ../src-org/sys/modules/udf2/Makefile	1970-01-01 09:00:00.000000000 +0900
+++ sys/modules/udf2/Makefile	2018-10-05 09:56:25.693219000 +0900
@@ -0,0 +1,12 @@
+# $FreeBSD: src/sys/modules/udf/Makefile,v 1.5 2004/01/13 11:28:50 ru Exp $
+
+.PATH: ${.CURDIR}/../../fs/udf2
+
+KMOD=	udf2
+
+SRCS=	udf_readwrite.c udf_subr.c udf_allocation.c \
+	udf_osta.c udf_vfsops.c udf_vnops.c udf_filenames.c
+SRCS+=	vnode_if.h
+EXPORT_SYMS=	udf_iconv
+
+.include <bsd.kmod.mk>
diff -ruPx *.orig ../src-org/sys/modules/udf2_iconv/Makefile sys/modules/udf2_iconv/Makefile
--- ../src-org/sys/modules/udf2_iconv/Makefile	1970-01-01 09:00:00.000000000 +0900
+++ sys/modules/udf2_iconv/Makefile	2018-10-05 09:56:25.694041000 +0900
@@ -0,0 +1,9 @@
+# $FreeBSD: src/sys/modules/udf_iconv/Makefile,v 1.1 2003/11/07 09:38:05 scottl Exp $
+
+.PATH:  ${.CURDIR}/../../fs/udf2
+KMOD=	udf2_iconv
+SRCS=	udf_iconv.c
+
+CFLAGS+= -I${.CURDIR}/../../
+
+.include <bsd.kmod.mk>
diff -ruPx *.orig ../src-org/sys/sys/udfio.h sys/sys/udfio.h
--- ../src-org/sys/sys/udfio.h	1970-01-01 09:00:00.000000000 +0900
+++ sys/sys/udfio.h	2018-10-05 09:56:25.696162000 +0900
@@ -0,0 +1,29 @@
+
+
+/* Shared between kernel & process */
+
+#ifndef        _SYS_UDFIO_H_
+#define        _SYS_UDFIO_H_
+
+#ifndef _KERNEL
+#include <sys/types.h>
+#endif
+#include <sys/ioccom.h>
+
+struct udf_session_info {
+       uint16_t num_sessions;
+       uint16_t sector_size;
+       uint16_t num_tracks;
+       uint8_t  first_track;
+
+       uint32_t session_num;
+       uint32_t session_start_addr;
+       uint32_t session_end_addr;
+       uint32_t session_last_written;
+       uint16_t session_first_track;
+       uint8_t  session_first_track_blank;
+       uint16_t session_last_track;
+};
+#define        UDFIOREADSESSIONINFO    _IOWR('c',300, struct udf_session_info)
+
+#endif /* !_SYS_UDFIO_H_ */
diff -ruPx *.orig ../src-org/sys/cam/scsi/scsi_cd.c sys/cam/scsi/scsi_cd.c
--- ../src-org/sys/cam/scsi/scsi_cd.c	2020-01-10 21:21:23.986972000 +0900
+++ sys/cam/scsi/scsi_cd.c	2019-12-20 08:49:28.193690000 +0900
@@ -62,6 +62,7 @@
 #include <sys/cdio.h>
 #include <sys/cdrio.h>
 #include <sys/dvdio.h>
+#include <sys/udfio.h>
 #include <sys/devicestat.h>
 #include <sys/proc.h>
 #include <sys/sbuf.h>
@@ -304,6 +305,19 @@
 					   struct dvd_struct *dvdstruct);
 static	callout_func_t	cdmediapoll;
 
+/* added for UDF */
+static int		udfreaddiscinfo(struct cam_periph *periph, 
+                                       struct udf_session_info *usi);
+static int		cdgetconf(struct cam_periph *periph, u_int8_t *data,
+                                 u_int32_t len, uint8_t rt, 
+                                 uint16_t startfeature, u_int32_t sense_flags);
+static int		cdreaddiscinfo(struct cam_periph *periph, uint8_t *data,
+                                      uint32_t len, uint32_t sense_flags);
+static int		cdreadtrackinfo(struct cam_periph *periph, 
+                                       uint8_t *data, uint32_t len, 
+                                       uint32_t trackno, 
+                                       uint32_t sense_flags);
+
 static struct periph_driver cddriver =
 {
 	cdinit, "cd",
@@ -2591,6 +2605,18 @@
 
 		break;
 	}
+       case UDFIOREADSESSIONINFO: {
+               struct udf_session_info  *usi;
+
+               usi = (struct udf_session_info *)addr;
+
+               CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
+                         ("trying to do UDFIOREADDISCINFO\n"));
+               
+               error = udfreaddiscinfo(periph, usi);
+
+               break;
+               }
 	default:
 		cam_periph_lock(periph);
 		error = cam_periph_ioctl(periph, cmd, addr, cderror);
@@ -2609,6 +2635,246 @@
 
 	return (error);
 }
+
+/* Added for UDF. */
+static int
+udfreaddiscinfo(struct cam_periph *periph, struct udf_session_info *usi)
+{
+       struct scsi_read_disc_info_data *rdi;
+       struct scsi_read_track_info_data *ti;
+       struct scsi_get_conf_header *gch;
+       struct scsi_get_conf_feature_inc_stream_write *lf;
+       int error, linksize, lra_valid, diskstate;
+       int sessionstatus;
+       uint32_t track_start_addr, track_size, free_blocks, lra;
+       uint16_t track, sessret;
+       uint8_t lsb, msb;
+
+       rdi = malloc(sizeof(*rdi), M_SCSICD, M_WAITOK | M_ZERO);
+       ti = malloc(sizeof(*ti), M_SCSICD, M_WAITOK | M_ZERO);
+       gch = malloc(sizeof(*gch) + sizeof(*lf), M_SCSICD, M_WAITOK | M_ZERO);
+
+       cam_periph_lock(periph);
+
+       /* Get link size. */
+       linksize = 7; // for CD-Roms
+       
+       error = cdgetconf(periph, (u_int8_t *)gch, sizeof(*gch) + sizeof(*lf),
+           GC_RT_ONEFEATURE, GC_FC_INCR_STREAMING_WRITABLE, SF_NO_PRINT);
+       lf = (struct scsi_get_conf_feature_inc_stream_write *)(gch + 1);
+       if (error)
+               goto out;
+
+       if (lf->byte3 & GC_CURRENT)
+               if (lf->num_link_sizes > 0) 
+                       linksize = lf->first_linksize;
+
+       /* Read get config and header data. */
+       error = cdreaddiscinfo(periph, (uint8_t *)rdi, sizeof (*rdi), 
+           SF_NO_PRINT);
+       if (error != 0)
+               goto out;
+
+       usi->num_sessions = rdi->num_sessions_lsb | 
+           (rdi->num_sessions_msb << 8);
+
+       if (usi->session_num == 0) {
+               diskstate = rdi->byte2 & 0x3;
+               sessionstatus = (rdi->byte2 >> 2) & 0x3;
+
+               // if the session is empty, we don't want to read it.
+               if (diskstate < 2 && sessionstatus == 0 && 
+                   usi->num_sessions > 1)
+                       usi->session_num = usi->num_sessions - 1;
+               else
+                       usi->session_num = usi->num_sessions;
+       }
+
+       usi->first_track = rdi->num_first_track;
+
+       lsb = rdi->last_track_last_session_lsb;
+       msb = rdi->last_track_last_session_msb;
+       usi->num_tracks = ((msb << 8) | lsb) - usi->first_track + 1;
+
+       usi->session_first_track = 0;
+       usi->session_last_track = 0;
+       usi->session_start_addr = 0;
+       track_start_addr = 0;
+       track_size = 0;
+       free_blocks = 0;
+       lra = 0;
+       lra_valid = 0;
+       for (track = usi->first_track; track <= usi->num_tracks; track++) {
+               error = cdreadtrackinfo(periph, (uint8_t *)ti, sizeof(*ti), 
+                               track, /*sense_flags*/SF_NO_PRINT);
+               if (error != 0) {
+                       goto out;
+               }
+               sessret = (ti->session_num_msb << 8) | ti->session_num_lsb;
+               if (sessret == usi->session_num) {
+                       if (usi->session_first_track == 0) {
+                               usi->session_first_track = track;
+                               usi->session_start_addr = 
+                                   scsi_4btoul(ti->track_start_addr);
+                               usi->session_first_track_blank = 
+                                   ti->track_info2 & READ_TRACK_BLANK ? 1 : 0;
+                       }
+                       usi->session_last_track = track;
+                       track_start_addr = scsi_4btoul(ti->track_start_addr);
+                       track_size = scsi_4btoul(ti->track_size);
+                       free_blocks = scsi_4btoul(ti->free_blocks);
+                       lra_valid = ti->valid_data & READ_TRACK_INFO_LRA_V;
+                       lra = scsi_4btoul(ti->last_recorded_addr);
+               } else if (usi->session_first_track != 0)
+                       break;
+       }
+       
+       if (usi->session_first_track == 0 || usi->session_last_track == 0) {
+               error = EINVAL;
+               goto out;
+       }
+
+       /* Calculate end address of session. */
+       usi->session_end_addr = track_start_addr + track_size - 1;
+       
+       /* Only needed for sequentially written disk, but populate it anyway.*/
+       if (lra_valid != 0)
+               usi->session_last_written = lra;
+       else {
+               usi->session_last_written = usi->session_end_addr;
+               if (free_blocks)
+                       usi->session_last_written -= free_blocks + linksize;
+       }
+
+       usi->sector_size = 2048; /* Almost all CDs have this block size. */
+out:
+       cam_periph_unlock(periph);
+       free(rdi, M_SCSICD);
+       free(ti, M_SCSICD);
+       free(gch, M_SCSICD);
+
+       if (error != 0)
+               error = ENODEV;
+
+       return (error);
+}
+
+static int 
+cdgetconf(struct cam_periph *periph, uint8_t *data, uint32_t len,
+         uint8_t rt, uint16_t startfeature, uint32_t sense_flags)
+{
+       struct scsi_get_conf *scsi_cmd;
+        struct ccb_scsiio *csio;
+       union ccb *ccb;
+       int error;
+
+       error = 0;
+       ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
+       csio = &ccb->csio;
+
+       cam_fill_csio(csio, 
+                     /* retries */ cd_retry_count, 
+                     /* cbfcnp */ cddone, 
+                     /* flags */ CAM_DIR_IN,
+                     /* tag_action */ MSG_SIMPLE_Q_TAG,
+                     /* data_ptr */ data,
+                     /* dxfer_len */ len,
+                     /* sense_len */ SSD_FULL_SIZE,
+                     sizeof(struct scsi_get_conf),
+                     /* timeout */ 50000);
+
+       scsi_cmd = (struct scsi_get_conf *)&csio->cdb_io.cdb_bytes;
+       bzero (scsi_cmd, sizeof(*scsi_cmd));
+       scsi_cmd->op_code = GET_CONFIGURATION;
+       scsi_cmd->byte2 = rt;
+       scsi_ulto2b(startfeature, scsi_cmd->start_feature);
+       scsi_ulto2b(len, scsi_cmd->alloc_len);
+
+       error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
+                        /*sense_flags*/SF_RETRY_UA | sense_flags);
+
+       xpt_release_ccb(ccb);
+
+       return (error);
+}
+
+static int
+cdreaddiscinfo(struct cam_periph *periph, uint8_t *data, uint32_t len, 
+              uint32_t sense_flags)
+{
+       struct scsi_read_disc_info *scsi_cmd;
+        struct ccb_scsiio *csio;
+       union ccb *ccb;
+       int error;
+
+       error = 0;
+	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
+       csio = &ccb->csio;
+
+       cam_fill_csio(csio, 
+                     /* retries */ cd_retry_count, 
+                     /* cbfcnp */ cddone, 
+                     /* flags */ CAM_DIR_IN,
+                     /* tag_action */ MSG_SIMPLE_Q_TAG,
+                     /* data_ptr */ data,
+                     /* dxfer_len */ len,
+                     /* sense_len */ SSD_FULL_SIZE,
+                     sizeof(struct scsi_read_disc_info),
+                     /* timeout */ 50000);
+
+       scsi_cmd = (struct scsi_read_disc_info *)&csio->cdb_io.cdb_bytes;
+       bzero (scsi_cmd, sizeof(*scsi_cmd));
+       scsi_cmd->op_code = READ_DISC_INFO;
+       scsi_ulto2b(len, scsi_cmd->alloc_len);
+
+       error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
+                        /*sense_flags*/SF_RETRY_UA | sense_flags);
+
+       xpt_release_ccb(ccb);
+
+       return (error);
+}
+
+static int
+cdreadtrackinfo(struct cam_periph *periph, uint8_t *data, uint32_t len, 
+              uint32_t trackno, uint32_t sense_flags)
+{
+       struct scsi_read_track_info *scsi_cmd;
+        struct ccb_scsiio *csio;
+       union ccb *ccb;
+       int error;
+
+       error = 0;
+	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
+       csio = &ccb->csio;
+
+       cam_fill_csio(csio, 
+                     /* retries */ cd_retry_count, 
+                     /* cbfcnp */ cddone, 
+                     /* flags */ CAM_DIR_IN,
+                     /* tag_action */ MSG_SIMPLE_Q_TAG,
+                     /* data_ptr */ data,
+                     /* dxfer_len */ len,
+                     /* sense_len */ SSD_FULL_SIZE,
+                     sizeof(struct scsi_read_track_info),
+                     /* timeout */ 50000);
+
+       scsi_cmd = (struct scsi_read_track_info *)&csio->cdb_io.cdb_bytes;
+       bzero (scsi_cmd, sizeof(*scsi_cmd));
+       scsi_cmd->op_code = READ_TRACK_INFO;
+       scsi_cmd->byte2 = READ_TRACK_INFO_AT_TRACK;
+       scsi_ulto4b(trackno, scsi_cmd->address);
+       scsi_ulto2b(len, scsi_cmd->alloc_len);
+
+       error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
+                        /*sense_flags*/SF_RETRY_UA | sense_flags);
+
+       xpt_release_ccb(ccb);
+
+       return (error);
+}
+/* End of UDF code. */
+
 
 static void
 cdprevent(struct cam_periph *periph, int action)
diff -ruPx *.orig ../src-org/sys/cam/scsi/scsi_cd.h sys/cam/scsi/scsi_cd.h
--- ../src-org/sys/cam/scsi/scsi_cd.h	2020-01-10 21:21:23.985273000 +0900
+++ sys/cam/scsi/scsi_cd.h	2019-09-01 20:13:55.695107000 +0900
@@ -135,6 +135,109 @@
 	uint8_t slots_length[2];
 };
 
+
+/* Used in UDF */
+#define GET_CONFIGURATION 0x46 /* cdrom read TOC */
+struct scsi_get_conf 
+{
+       uint8_t op_code;
+       uint8_t byte2;
+#define GC_RT_ONEFEATURE       0x02 /* Return header and zero or one feature. */
+       uint8_t start_feature[2];
+#define GC_FC_INCR_STREAMING_WRITABLE  0x0021
+       uint8_t reserved[3];
+       uint8_t alloc_len[2];
+       uint8_t control;
+};
+
+struct scsi_get_conf_header
+{
+       uint8_t data_len[4];
+       uint8_t reserved[2];
+       uint8_t cur_profile[2];
+};
+
+struct scsi_get_conf_feature_inc_stream_write
+{
+       uint8_t feature_code[2];
+       uint8_t byte3;
+#define GC_CURRENT                     0x1
+       uint8_t additional_len;
+       uint8_t data_block_types_supported[2];
+       uint8_t byte7;
+       uint8_t num_link_sizes;
+       uint8_t first_linksize;
+       uint8_t other_linksizes[1];     /* one byte each, most return 1. */
+};
+
+#define READ_DISC_INFO 0x51
+struct scsi_read_disc_info
+{
+       uint8_t op_code;
+       uint8_t byte2;
+       uint8_t reserved[5];
+       uint8_t alloc_len[2];
+       uint8_t control;
+};
+
+struct scsi_read_disc_info_data
+{
+       uint8_t disc_info_len[2];
+       uint8_t byte2;
+       uint8_t num_first_track;
+       uint8_t num_sessions_lsb;
+       uint8_t first_track_last_session_lsb;
+       uint8_t last_track_last_session_lsb;
+       uint8_t byte8;
+       uint8_t disc_type;
+       uint8_t num_sessions_msb;
+       uint8_t first_track_last_session_msb;
+       uint8_t last_track_last_session_msb;
+       uint8_t disc_id[4];
+       uint8_t leadin_start_last_session[4];
+       uint8_t last_start_time_leadout[4];
+       uint8_t disc_bar_code[8];
+       uint8_t reserved;
+       uint8_t num_opc_entries;
+       uint8_t opc_entries[];
+};
+
+#define READ_TRACK_INFO 0x52
+struct scsi_read_track_info
+{
+       uint8_t op_code;
+       uint8_t byte2;
+#define READ_TRACK_INFO_AT_TRACK       0x01
+       uint8_t address[4];
+       uint8_t reserved;
+       uint8_t alloc_len[2];
+       uint8_t control;
+};
+
+struct scsi_read_track_info_data
+{
+       uint8_t track_info_len[2];
+       uint8_t track_num_lsb;
+       uint8_t session_num_lsb;
+       uint8_t reserved1;
+       uint8_t track_info1;
+       uint8_t track_info2;
+#define READ_TRACK_BLANK               0x40
+       uint8_t valid_data;
+#define READ_TRACK_INFO_LRA_V          0x2
+#define READ_TRACK_INFO_NWA_V          0x1
+       uint8_t track_start_addr[4];
+       uint8_t next_writable_addr[4];
+       uint8_t free_blocks[4];
+       uint8_t packet_size[4];
+       uint8_t track_size[4];
+       uint8_t last_recorded_addr[4];
+       uint8_t track_num_msb;
+       uint8_t session_num_msb;
+       uint8_t reserved2[2];
+};
+/* End of UDF */
+
 struct scsi_pause
 {
 	u_int8_t op_code;
