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 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 + +.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +.\" Jeroen Ruigrok van der Werven +.\" 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 * + * Copyright (c) 2001, 2002 Scott Long + * 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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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 +__FBSDID("$FreeBSD: src/sys/fs/udf/udf_iconv.c,v 1.1 2003/11/05 06:56:08 scottl Exp $"); + +#include +#include +#include +#include +#include + +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 + +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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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, §or, &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, §or, &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, §or, &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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if 0 +#include +#endif +#include +#include + +#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(void) +{ + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 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 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 +#endif +#include + +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 #include #include +#include #include #include #include @@ -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; --- sys/kern/kern_sysctl.c.old 2026-07-24 22:15:27.663946000 +0900 +++ sys/kern/kern_sysctl.c 2026-07-24 22:23:21.849025000 +0900 @@ -518,8 +518,13 @@ /* check for non-auto OID number collision */ if (oidp->oid_number >= 0 && oidp->oid_number < CTL_AUTO_START && oid_number >= CTL_AUTO_START) { - panic("sysctl: OID number(%d) is already in use for '%s'\n", - oidp->oid_number, oidp->oid_name); + if (strcmp(oidp->oid_name, "udf2")) { + panic("sysctl: OID number(%d) is already in use for '%s'\n", + oidp->oid_number, oidp->oid_name); + } else { + printf("sysctl: OID number(%d) is already in use for '%s'\n", + oidp->oid_number, oidp->oid_name); + } } /* update the OID number, if any */ oidp->oid_number = oid_number;