aboutsummaryrefslogtreecommitdiff
path: root/sys/fs/udf
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/udf')
-rw-r--r--sys/fs/udf/ecma167-udf.h388
-rw-r--r--sys/fs/udf/osta.c512
-rw-r--r--sys/fs/udf/osta.h43
-rw-r--r--sys/fs/udf/udf.h128
-rw-r--r--sys/fs/udf/udf_iconv.c38
-rw-r--r--sys/fs/udf/udf_mount.h31
-rw-r--r--sys/fs/udf/udf_vfsops.c825
-rw-r--r--sys/fs/udf/udf_vnops.c1496
8 files changed, 3461 insertions, 0 deletions
diff --git a/sys/fs/udf/ecma167-udf.h b/sys/fs/udf/ecma167-udf.h
new file mode 100644
index 000000000000..f920e08202dc
--- /dev/null
+++ b/sys/fs/udf/ecma167-udf.h
@@ -0,0 +1,388 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* ecma167-udf.h */
+/* Structure/definitions/constants a la ECMA 167 rev. 3 */
+
+/* Tag identifiers */
+enum {
+ 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_FENTRY = 261
+};
+
+/* 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;
+
+/* Recorded Address [4/7.1] */
+struct lb_addr {
+ 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 pos;
+} __packed;
+
+/* Long Allocation Descriptor [4/14.14.2] */
+struct long_ad {
+ uint32_t len;
+ struct lb_addr loc;
+ uint16_t ad_flags;
+ uint32_t ad_id;
+} __packed;
+
+/* Extended Allocation Descriptor [4/14.14.3] */
+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;
+
+union icb {
+ struct short_ad s_ad;
+ struct long_ad l_ad;
+ struct ext_ad e_ad;
+};
+
+/* Character set spec [1/7.2.1] */
+struct charspec {
+ uint8_t type;
+ uint8_t inf[63];
+} __packed;
+
+/* 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;
+
+/* 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_SETUID 0x40
+#define UDF_ICB_TAG_FLAGS_SETGID 0x80
+#define UDF_ICB_TAG_FLAGS_STICKY 0x100
+
+/* Anchor Volume Descriptor Pointer [3/10.2] */
+struct anchor_vdp {
+ struct desc_tag tag;
+ struct extent_ad main_vds_ex;
+ struct extent_ad reserve_vds_ex;
+} __packed;
+
+/* Volume Descriptor Pointer [3/10.3] */
+struct vol_desc_ptr {
+ struct desc_tag tag;
+ uint32_t vds_number;
+ struct extent_ad next_vds_ex;
+} __packed;
+
+/* Primary Volume Descriptor [3/10.1] */
+struct pri_vol_desc {
+ struct desc_tag tag;
+ uint32_t seq_num;
+ uint32_t pdv_num;
+ char vol_id[32];
+ uint16_t vds_num;
+ uint16_t max_vol_seq;
+ uint16_t ichg_lvl;
+ uint16_t max_ichg_lvl;
+ uint32_t charset_list;
+ uint32_t max_charset_list;
+ char volset_id[128];
+ struct charspec desc_charset;
+ 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_lov;
+ uint16_t flags;
+ uint8_t reserved[22];
+} __packed;
+
+/* Logical Volume Descriptor [3/10.6] */
+struct logvol_desc {
+ struct desc_tag tag;
+ uint32_t seq_num;
+ struct charspec desc_charset;
+ char logvol_id[128];
+ uint32_t lb_size;
+ struct regid domain_id;
+ union {
+ struct long_ad fsd_loc;
+ 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_id;
+ uint8_t maps[1];
+} __packed;
+
+/* 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;
+
+#define UDF_PMAP_TYPE1_SIZE 6
+
+/* Type 2 Partition Map [3/10.7.3] */
+struct part_map_2 {
+ uint8_t type;
+ uint8_t len;
+ uint8_t part_id[62];
+} __packed;
+
+#define UDF_PMAP_TYPE2_SIZE 64
+
+/* 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 Sparing Tables */
+ uint8_t reserved1;
+ uint32_t st_size;
+ uint32_t st_loc[1];
+} __packed;
+
+union udf_pmap {
+ struct part_map_1 pm1;
+ struct part_map_2 pm2;
+ struct part_map_virt pmv;
+ struct part_map_spare pms;
+};
+
+/* Sparing Map Entry [UDF 2.01/2.2.11] */
+struct spare_map_entry {
+ uint32_t org;
+ uint32_t map;
+} __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;
+
+/* Partition Descriptor [3/10.5] */
+struct part_desc {
+ struct desc_tag tag;
+ uint32_t seq_num;
+ uint16_t flags;
+ uint16_t part_num;
+ struct regid contents;
+ uint8_t contents_use[128];
+ uint32_t access_type;
+ uint32_t start_loc;
+ uint32_t part_len;
+ struct regid imp_id;
+ uint8_t imp_use[128];
+ uint8_t reserved[156];
+} __packed;
+
+/* 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;
+ uint32_t fileset_desc_num;
+ struct charspec logvol_id_charset;
+ char logvol_id[128];
+ struct charspec fileset_charset;
+ char fileset_id[32];
+ char copyright_file_id[32];
+ char abstract_file_id[32];
+ struct long_ad rootdir_icb;
+ struct regid domain_id;
+ struct long_ad next_ex;
+ struct long_ad streamdir_icb;
+ uint8_t reserved[32];
+} __packed;
+
+/* File Identifier Descriptor [4/14.4] */
+struct fileid_desc {
+ struct desc_tag tag;
+ uint16_t file_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[1];
+} __packed;
+#define UDF_FID_SIZE 38
+#define UDF_FILE_CHAR_VIS (1 << 0) /* Visible */
+#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 */
+
+/* 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
+
+/* Path Component [4/14.16.1] */
+struct path_component {
+ uint8_t type;
+ uint8_t length;
+ uint16_t version;
+ uint8_t identifier[1];
+} __packed;
+#define UDF_PATH_ROOT 2
+#define UDF_PATH_DOTDOT 3
+#define UDF_PATH_DOT 4
+#define UDF_PATH_PATH 5
+
+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 part_desc pd;
+ struct fileset_desc fsd;
+ struct fileid_desc fid;
+ struct file_entry fe;
+};
+
+/* Useful defines */
+
+#define GETICB(ad_type, fentry, offset) \
+ (struct ad_type *)&fentry->data[offset]
+
+#define GETICBLEN(ad_type, icb) le32toh(((struct ad_type *)(icb))->len)
diff --git a/sys/fs/udf/osta.c b/sys/fs/udf/osta.c
new file mode 100644
index 000000000000..70a22d933652
--- /dev/null
+++ b/sys/fs/udf/osta.c
@@ -0,0 +1,512 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <fs/udf/osta.h>
+
+/*****************************************************************************/
+/*-
+ **********************************************************************
+ * OSTA compliant Unicode compression, uncompression routines.
+ * Copyright 1995 Micro Design International, Inc.
+ * Written by Jason M. Rinn.
+ * Micro Design International gives permission for the free use of the
+ * following source code.
+ */
+
+/***********************************************************************
+ * Takes an OSTA CS0 compressed unicode name, and converts
+ * it to Unicode.
+ * The Unicode output will be in the byte order
+ * that the local compiler uses for 16-bit values.
+ * NOTE: This routine only performs error checking on the compID.
+ * It is up to the user to ensure that the unicode buffer is large
+ * enough, and that the compressed unicode name is correct.
+ *
+ * RETURN VALUE
+ *
+ * The number of unicode characters which were uncompressed.
+ * A -1 is returned if the compression ID is invalid.
+ */
+int
+udf_UncompressUnicode(
+ int numberOfBytes, /* (Input) number of bytes read from media. */
+ byte *UDFCompressed, /* (Input) bytes read from media. */
+ unicode_t *unicode) /* (Output) uncompressed unicode characters. */
+{
+ unsigned int compID;
+ int returnValue, unicodeIndex, byteIndex;
+
+ /* Use UDFCompressed to store current byte being read. */
+ compID = UDFCompressed[0];
+
+ /* First check for valid compID. */
+ if (compID != 8 && compID != 16) {
+ returnValue = -1;
+ } else {
+ unicodeIndex = 0;
+ byteIndex = 1;
+
+ /* Loop through all the bytes. */
+ while (byteIndex < numberOfBytes) {
+ if (compID == 16) {
+ /* Move the first byte to the high bits of the
+ * unicode char.
+ */
+ unicode[unicodeIndex] =
+ UDFCompressed[byteIndex++] << 8;
+ } else {
+ unicode[unicodeIndex] = 0;
+ }
+ if (byteIndex < numberOfBytes) {
+ /*Then the next byte to the low bits. */
+ unicode[unicodeIndex] |=
+ UDFCompressed[byteIndex++];
+ }
+ unicodeIndex++;
+ }
+ returnValue = unicodeIndex;
+ }
+ return(returnValue);
+}
+
+/*
+ * Almost same as udf_UncompressUnicode(). The difference is that
+ * it keeps byte order of unicode string.
+ */
+int
+udf_UncompressUnicodeByte(
+ int numberOfBytes, /* (Input) number of bytes read from media. */
+ byte *UDFCompressed, /* (Input) bytes read from media. */
+ byte *unicode) /* (Output) uncompressed unicode characters. */
+{
+ unsigned int compID;
+ int returnValue, unicodeIndex, byteIndex;
+
+ /* Use UDFCompressed to store current byte being read. */
+ compID = UDFCompressed[0];
+
+ /* First check for valid compID. */
+ if (compID != 8 && compID != 16) {
+ returnValue = -1;
+ } else {
+ unicodeIndex = 0;
+ byteIndex = 1;
+
+ /* Loop through all the bytes. */
+ while (byteIndex < numberOfBytes) {
+ if (compID == 16) {
+ /* Move the first byte to the high bits of the
+ * unicode char.
+ */
+ unicode[unicodeIndex++] =
+ UDFCompressed[byteIndex++];
+ } else {
+ unicode[unicodeIndex++] = 0;
+ }
+ if (byteIndex < numberOfBytes) {
+ /*Then the next byte to the low bits. */
+ unicode[unicodeIndex++] =
+ UDFCompressed[byteIndex++];
+ }
+ }
+ returnValue = unicodeIndex;
+ }
+ return(returnValue);
+}
+
+/***********************************************************************
+ * DESCRIPTION:
+ * Takes a string of unicode wide characters and returns an OSTA CS0
+ * compressed unicode string. The unicode MUST be in the byte order of
+ * the compiler in order to obtain correct results. Returns an error
+ * if the compression ID is invalid.
+ *
+ * NOTE: This routine assumes the implementation already knows, by
+ * the local environment, how many bits are appropriate and
+ * therefore does no checking to test if the input characters fit
+ * into that number of bits or not.
+ *
+ * RETURN VALUE
+ *
+ * The total number of bytes in the compressed OSTA CS0 string,
+ * including the compression ID.
+ * A -1 is returned if the compression ID is invalid.
+ */
+int
+udf_CompressUnicode(
+ int numberOfChars, /* (Input) number of unicode characters. */
+ int compID, /* (Input) compression ID to be used. */
+ unicode_t *unicode, /* (Input) unicode characters to compress. */
+ byte *UDFCompressed) /* (Output) compressed string, as bytes. */
+{
+ int byteIndex, unicodeIndex;
+
+ if (compID != 8 && compID != 16) {
+ byteIndex = -1; /* Unsupported compression ID ! */
+ } else {
+ /* Place compression code in first byte. */
+ UDFCompressed[0] = compID;
+
+ byteIndex = 1;
+ unicodeIndex = 0;
+ while (unicodeIndex < numberOfChars) {
+ if (compID == 16) {
+ /* First, place the high bits of the char
+ * into the byte stream.
+ */
+ UDFCompressed[byteIndex++] =
+ (unicode[unicodeIndex] & 0xFF00) >> 8;
+ }
+ /*Then place the low bits into the stream. */
+ UDFCompressed[byteIndex++] =
+ unicode[unicodeIndex] & 0x00FF;
+ unicodeIndex++;
+ }
+ }
+ return(byteIndex);
+}
+
+/*****************************************************************************/
+/*
+ * CRC 010041
+ */
+static unsigned short crc_table[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+ 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+ 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+ 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+ 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+ 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+ 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+ 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+ 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+ 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+ 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+ 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+ 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+ 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+ 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+ 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+ 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+ 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+ 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+ 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+ 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+ 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+ 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+
+unsigned short
+udf_cksum(s, n)
+ unsigned char *s;
+ int n;
+{
+ unsigned short crc=0;
+
+ while (n-- > 0)
+ crc = crc_table[(crc>>8 ^ *s++) & 0xff] ^ (crc<<8);
+ return crc;
+}
+
+/* UNICODE Checksum */
+unsigned short
+udf_unicode_cksum(s, n)
+ unsigned short *s;
+ int n;
+{
+ unsigned short crc=0;
+
+ while (n-- > 0) {
+ /* Take high order byte first--corresponds to a big endian
+ * byte stream.
+ */
+ crc = crc_table[(crc>>8 ^ (*s>>8)) & 0xff] ^ (crc<<8);
+ crc = crc_table[(crc>>8 ^ (*s++ & 0xff)) & 0xff] ^ (crc<<8);
+ }
+ return crc;
+}
+
+#ifdef MAIN
+unsigned char bytes[] = { 0x70, 0x6A, 0x77 };
+
+main()
+{
+ unsigned short x;
+ x = cksum(bytes, sizeof bytes);
+ printf("checksum: calculated=%4.4x, correct=%4.4x\en", x, 0x3299);
+ exit(0);
+}
+#endif
+
+/*****************************************************************************/
+#ifdef NEEDS_ISPRINT
+/*-
+ **********************************************************************
+ * OSTA UDF compliant file name translation routine for OS/2,
+ * Windows 95, Windows NT, Macintosh and UNIX.
+ * Copyright 1995 Micro Design International, Inc.
+ * Written by Jason M. Rinn.
+ * Micro Design International gives permission for the free use of the
+ * following source code.
+ */
+
+/***********************************************************************
+ * To use these routines with different operating systems.
+ *
+ * OS/2
+ * Define OS2
+ * Define MAXLEN = 254
+ *
+ * Windows 95
+ * Define WIN_95
+ * Define MAXLEN = 255
+ *
+ * Windows NT
+ * Define WIN_NT
+ * Define MAXLEN = 255
+ *
+ * Macintosh:
+ * Define APPLE_MAC.
+ * Define MAXLEN = 31.
+ *
+ * UNIX
+ * Define UNIX.
+ * Define MAXLEN as specified by unix version.
+ */
+
+#define ILLEGAL_CHAR_MARK 0x005F
+#define CRC_MARK 0x0023
+#define EXT_SIZE 5
+#define TRUE 1
+#define FALSE 0
+#define PERIOD 0x002E
+#define SPACE 0x0020
+
+/*** PROTOTYPES ***/
+int IsIllegal(unicode_t ch);
+
+/* Define a function or macro which determines if a Unicode character is
+ * printable under your implementation.
+ */
+int UnicodeIsPrint(unicode_t);
+
+/***********************************************************************
+ * Translates a long file name to one using a MAXLEN and an illegal
+ * char set in accord with the OSTA requirements. Assumes the name has
+ * already been translated to Unicode.
+ *
+ * RETURN VALUE
+ *
+ * Number of unicode characters in translated name.
+ */
+int UDFTransName(
+ unicode_t *newName, /* (Output)Translated name. Must be of length
+ * MAXLEN */
+ unicode_t *udfName, /* (Input) Name from UDF volume.*/
+ int udfLen) /* (Input) Length of UDF Name. */
+{
+ int index, newIndex = 0, needsCRC = FALSE;
+ int extIndex = 0, newExtIndex = 0, hasExt = FALSE;
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+ int trailIndex = 0;
+#endif
+ unsigned short valueCRC;
+ unicode_t current;
+ const char hexChar[] = "0123456789ABCDEF";
+
+ for (index = 0; index < udfLen; index++) {
+ current = udfName[index];
+
+ if (IsIllegal(current) || !UnicodeIsPrint(current)) {
+ needsCRC = TRUE;
+ /* Replace Illegal and non-displayable chars with
+ * underscore.
+ */
+ current = ILLEGAL_CHAR_MARK;
+ /* Skip any other illegal or non-displayable
+ * characters.
+ */
+ while(index+1 < udfLen && (IsIllegal(udfName[index+1])
+ || !UnicodeIsPrint(udfName[index+1]))) {
+ index++;
+ }
+ }
+
+ /* Record position of extension, if one is found. */
+ if (current == PERIOD && (udfLen - index -1) <= EXT_SIZE) {
+ if (udfLen == index + 1) {
+ /* A trailing period is NOT an extension. */
+ hasExt = FALSE;
+ } else {
+ hasExt = TRUE;
+ extIndex = index;
+ newExtIndex = newIndex;
+ }
+ }
+
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+ /* Record position of last char which is NOT period or space. */
+ else if (current != PERIOD && current != SPACE) {
+ trailIndex = newIndex;
+ }
+#endif
+
+ if (newIndex < MAXLEN) {
+ newName[newIndex++] = current;
+ } else {
+ needsCRC = TRUE;
+ }
+ }
+
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+ /* For OS2, 95 & NT, truncate any trailing periods and\or spaces. */
+ if (trailIndex != newIndex - 1) {
+ newIndex = trailIndex + 1;
+ needsCRC = TRUE;
+ hasExt = FALSE; /* Trailing period does not make an
+ * extension. */
+ }
+#endif
+
+ if (needsCRC) {
+ unicode_t ext[EXT_SIZE];
+ int localExtIndex = 0;
+ if (hasExt) {
+ int maxFilenameLen;
+ /* Translate extension, and store it in ext. */
+ for(index = 0; index<EXT_SIZE &&
+ extIndex + index +1 < udfLen; index++ ) {
+ current = udfName[extIndex + index + 1];
+ if (IsIllegal(current) ||
+ !UnicodeIsPrint(current)) {
+ needsCRC = 1;
+ /* Replace Illegal and non-displayable
+ * chars with underscore.
+ */
+ current = ILLEGAL_CHAR_MARK;
+ /* Skip any other illegal or
+ * non-displayable characters.
+ */
+ while(index + 1 < EXT_SIZE
+ && (IsIllegal(udfName[extIndex +
+ index + 2]) ||
+ !isprint(udfName[extIndex +
+ index + 2]))) {
+ index++;
+ }
+ }
+ ext[localExtIndex++] = current;
+ }
+
+ /* Truncate filename to leave room for extension and
+ * CRC.
+ */
+ maxFilenameLen = ((MAXLEN - 5) - localExtIndex - 1);
+ if (newIndex > maxFilenameLen) {
+ newIndex = maxFilenameLen;
+ } else {
+ newIndex = newExtIndex;
+ }
+ } else if (newIndex > MAXLEN - 5) {
+ /*If no extension, make sure to leave room for CRC. */
+ newIndex = MAXLEN - 5;
+ }
+ newName[newIndex++] = CRC_MARK; /* Add mark for CRC. */
+
+ /*Calculate CRC from original filename from FileIdentifier. */
+ valueCRC = udf_unicode_cksum(udfName, udfLen);
+ /* Convert 16-bits of CRC to hex characters. */
+ newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12];
+ newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8];
+ newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4];
+ newName[newIndex++] = hexChar[(valueCRC & 0x000f)];
+
+ /* Place a translated extension at end, if found. */
+ if (hasExt) {
+ newName[newIndex++] = PERIOD;
+ for (index = 0;index < localExtIndex ;index++ ) {
+ newName[newIndex++] = ext[index];
+ }
+ }
+ }
+ return(newIndex);
+}
+
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+/***********************************************************************
+ * Decides if a Unicode character matches one of a list
+ * of ASCII characters.
+ * Used by OS2 version of IsIllegal for readability, since all of the
+ * illegal characters above 0x0020 are in the ASCII subset of Unicode.
+ * Works very similarly to the standard C function strchr().
+ *
+ * RETURN VALUE
+ *
+ * Non-zero if the Unicode character is in the given ASCII string.
+ */
+int UnicodeInString(
+ unsigned char *string, /* (Input) String to search through. */
+ unicode_t ch) /* (Input) Unicode char to search for. */
+{
+ int found = FALSE;
+ while (*string != '\0' && found == FALSE) {
+ /* These types should compare, since both are unsigned
+ * numbers. */
+ if (*string == ch) {
+ found = TRUE;
+ }
+ string++;
+ }
+ return(found);
+}
+#endif /* OS2 */
+
+/***********************************************************************
+ * Decides whether the given character is illegal for a given OS.
+ *
+ * RETURN VALUE
+ *
+ * Non-zero if char is illegal.
+ */
+int IsIllegal(unicode_t ch)
+{
+#ifdef APPLE_MAC
+ /* Only illegal character on the MAC is the colon. */
+ if (ch == 0x003A) {
+ return(1);
+ } else {
+ return(0);
+ }
+
+#elif defined UNIX
+ /* Illegal UNIX characters are NULL and slash. */
+ if (ch == 0x0000 || ch == 0x002F) {
+ return(1);
+ } else {
+ return(0);
+ }
+
+#elif defined OS2 || defined WIN_95 || defined WIN_NT
+ /* Illegal char's for OS/2 according to WARP toolkit. */
+ if (ch < 0x0020 || UnicodeInString("\\/:*?\"<>|", ch)) {
+ return(1);
+ } else {
+ return(0);
+ }
+#endif
+}
+#endif
diff --git a/sys/fs/udf/osta.h b/sys/fs/udf/osta.h
new file mode 100644
index 000000000000..7a889c2bb891
--- /dev/null
+++ b/sys/fs/udf/osta.h
@@ -0,0 +1,43 @@
+/*
+ * Prototypes for the OSTA functions
+ *
+ * $FreeBSD$
+ */
+
+/*-
+ **********************************************************************
+ * OSTA compliant Unicode compression, uncompression routines.
+ * Copyright 1995 Micro Design International, Inc.
+ * Written by Jason M. Rinn.
+ * Micro Design International gives permission for the free use of the
+ * following source code.
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef UNIX
+#define UNIX
+#endif
+
+#ifndef MAXLEN
+#define MAXLEN 255
+#endif
+
+/***********************************************************************
+ * The following two typedef's are to remove compiler dependencies.
+ * byte needs to be unsigned 8-bit, and unicode_t needs to be
+ * unsigned 16-bit.
+ */
+typedef unsigned short unicode_t;
+typedef unsigned char byte;
+
+int udf_UncompressUnicode(int, byte *, unicode_t *);
+int udf_UncompressUnicodeByte(int, byte *, byte *);
+int udf_CompressUnicode(int, int, unicode_t *, byte *);
+unsigned short udf_cksum(unsigned char *, int);
+unsigned short udf_unicode_cksum(unsigned short *, int);
+int UDFTransName(unicode_t *, unicode_t *, int);
diff --git a/sys/fs/udf/udf.h b/sys/fs/udf/udf.h
new file mode 100644
index 000000000000..33bf4539c41b
--- /dev/null
+++ b/sys/fs/udf/udf.h
@@ -0,0 +1,128 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define UDF_HASHTBLSIZE 100
+
+struct udf_node {
+ struct vnode *i_vnode;
+ struct udf_mnt *udfmp;
+ ino_t hash_id;
+ long diroff;
+ struct file_entry *fentry;
+};
+
+struct udf_mnt {
+ int im_flags;
+ struct mount *im_mountp;
+ struct g_consumer *im_cp;
+ struct bufobj *im_bo;
+ struct cdev *im_dev;
+ struct vnode *im_devvp;
+ int bsize;
+ int bshift;
+ int bmask;
+ uint32_t part_start;
+ uint32_t part_len;
+ uint64_t root_id;
+ struct long_ad root_icb;
+ int p_sectors;
+ int s_table_entries;
+ struct udf_sparing_table *s_table;
+ void *im_d2l; /* disk->local iconv handle */
+#if 0
+ void *im_l2d; /* local->disk iconv handle */
+#endif
+};
+
+struct udf_dirstream {
+ struct udf_node *node;
+ struct udf_mnt *udfmp;
+ struct buf *bp;
+ uint8_t *data;
+ uint8_t *buf;
+ int fsize;
+ int off;
+ int this_off;
+ int offset;
+ int size;
+ int error;
+ int fid_fragment;
+};
+
+struct ifid {
+ u_short ifid_len;
+ u_short ifid_pad;
+ int ifid_ino;
+ long ifid_start;
+};
+
+#define VFSTOUDFFS(mp) ((struct udf_mnt *)((mp)->mnt_data))
+#define VTON(vp) ((struct udf_node *)((vp)->v_data))
+
+/*
+ * The block layer refers to things in terms of 512 byte blocks by default.
+ * btodb() is expensive, so speed things up.
+ * XXX Can the block layer be forced to use a different block size?
+ */
+#define RDSECTOR(devvp, sector, size, bp) \
+ bread(devvp, sector << (udfmp->bshift - DEV_BSHIFT), size, NOCRED, bp)
+
+MALLOC_DECLARE(M_UDFFENTRY);
+
+static __inline int
+udf_readdevblks(struct udf_mnt *udfmp, int sector, int size, struct buf **bp)
+{
+ return (RDSECTOR(udfmp->im_devvp, sector,
+ (size + udfmp->bmask) & ~udfmp->bmask, bp));
+}
+
+/*
+ * Produce a suitable file number from an ICB. The passed in ICB is expected
+ * to be in little endian (meaning that it hasn't been swapped for big
+ * endian machines yet).
+ * XXX If the fileno resolves to 0, we might be in big trouble.
+ * XXX Assumes the ICB is a long_ad. This struct is compatible with short_ad,
+ * but not ext_ad.
+ */
+static __inline ino_t
+udf_getid(struct long_ad *icb)
+{
+ return (le32toh(icb->loc.lb_num));
+}
+
+int udf_allocv(struct mount *, struct vnode **, struct thread *);
+int udf_checktag(struct desc_tag *, uint16_t);
+int udf_vget(struct mount *, ino_t, int, struct vnode **);
+
+extern uma_zone_t udf_zone_trans;
+extern uma_zone_t udf_zone_node;
+extern uma_zone_t udf_zone_ds;
+
+extern struct vop_vector udf_fifoops;
diff --git a/sys/fs/udf/udf_iconv.c b/sys/fs/udf/udf_iconv.c
new file mode 100644
index 000000000000..6ffdc01eea87
--- /dev/null
+++ b/sys/fs/udf/udf_iconv.c
@@ -0,0 +1,38 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2003 Ryuichiro Imura
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/iconv.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/mount.h>
+
+VFS_DECLARE_ICONV(udf);
diff --git a/sys/fs/udf/udf_mount.h b/sys/fs/udf/udf_mount.h
new file mode 100644
index 000000000000..8451510dc79f
--- /dev/null
+++ b/sys/fs/udf/udf_mount.h
@@ -0,0 +1,31 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#define UDFMNT_KICONV 0x00000001
diff --git a/sys/fs/udf/udf_vfsops.c b/sys/fs/udf/udf_vfsops.c
new file mode 100644
index 000000000000..45331dedab14
--- /dev/null
+++ b/sys/fs/udf/udf_vfsops.c
@@ -0,0 +1,825 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* udf_vfsops.c */
+/* Implement the VFS side of things */
+
+/*
+ * Ok, here's how it goes. The UDF specs are pretty clear on how each data
+ * structure is made up, but not very clear on how they relate to each other.
+ * Here is the skinny... This demostrates a filesystem with one file in the
+ * root directory. Subdirectories are treated just as normal files, but they
+ * have File Id Descriptors of their children as their file data. As for the
+ * Anchor Volume Descriptor Pointer, it can exist in two of the following three
+ * places: sector 256, sector n (the max sector of the disk), or sector
+ * n - 256. It's a pretty good bet that one will exist at sector 256 though.
+ * One caveat is unclosed CD media. For that, sector 256 cannot be written,
+ * so the Anchor Volume Descriptor Pointer can exist at sector 512 until the
+ * media is closed.
+ *
+ * Sector:
+ * 256:
+ * n: Anchor Volume Descriptor Pointer
+ * n - 256: |
+ * |
+ * |-->Main Volume Descriptor Sequence
+ * | |
+ * | |
+ * | |-->Logical Volume Descriptor
+ * | |
+ * |-->Partition Descriptor |
+ * | |
+ * | |
+ * |-->Fileset Descriptor
+ * |
+ * |
+ * |-->Root Dir File Entry
+ * |
+ * |
+ * |-->File data:
+ * File Id Descriptor
+ * |
+ * |
+ * |-->File Entry
+ * |
+ * |
+ * |-->File data
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/dirent.h>
+#include <sys/fcntl.h>
+#include <sys/iconv.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/vnode.h>
+#include <sys/endian.h>
+
+#include <geom/geom.h>
+#include <geom/geom_vfs.h>
+
+#include <vm/uma.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/osta.h>
+#include <fs/udf/udf.h>
+#include <fs/udf/udf_mount.h>
+
+static MALLOC_DEFINE(M_UDFMOUNT, "udf_mount", "UDF mount structure");
+MALLOC_DEFINE(M_UDFFENTRY, "udf_fentry", "UDF file entry structure");
+
+struct iconv_functions *udf_iconv = NULL;
+
+/* Zones */
+uma_zone_t udf_zone_trans = NULL;
+uma_zone_t udf_zone_node = NULL;
+uma_zone_t udf_zone_ds = NULL;
+
+static vfs_init_t udf_init;
+static vfs_uninit_t udf_uninit;
+static vfs_mount_t udf_mount;
+static vfs_root_t udf_root;
+static vfs_statfs_t udf_statfs;
+static vfs_unmount_t udf_unmount;
+static vfs_fhtovp_t udf_fhtovp;
+
+static int udf_find_partmaps(struct udf_mnt *, struct logvol_desc *);
+
+static struct vfsops udf_vfsops = {
+ .vfs_fhtovp = udf_fhtovp,
+ .vfs_init = udf_init,
+ .vfs_mount = udf_mount,
+ .vfs_root = udf_root,
+ .vfs_statfs = udf_statfs,
+ .vfs_uninit = udf_uninit,
+ .vfs_unmount = udf_unmount,
+ .vfs_vget = udf_vget,
+};
+VFS_SET(udf_vfsops, udf, VFCF_READONLY);
+
+MODULE_VERSION(udf, 1);
+
+static int udf_mountfs(struct vnode *, struct mount *);
+
+static int
+udf_init(struct vfsconf *foo)
+{
+
+ /*
+ * This code used to pre-allocate a certain number of pages for each
+ * pool, reducing the need to grow the zones later on. UMA doesn't
+ * advertise any such functionality, unfortunately =-<
+ */
+ udf_zone_trans = uma_zcreate("UDF translation buffer, zone", MAXNAMLEN *
+ sizeof(unicode_t), NULL, NULL, NULL, NULL, 0, 0);
+
+ udf_zone_node = uma_zcreate("UDF Node zone", sizeof(struct udf_node),
+ NULL, NULL, NULL, NULL, 0, 0);
+
+ udf_zone_ds = uma_zcreate("UDF Dirstream zone",
+ sizeof(struct udf_dirstream), NULL, NULL, NULL, NULL, 0, 0);
+
+ if ((udf_zone_node == NULL) || (udf_zone_trans == NULL) ||
+ (udf_zone_ds == NULL)) {
+ printf("Cannot create allocation zones.\n");
+ return (ENOMEM);
+ }
+
+ return 0;
+}
+
+static int
+udf_uninit(struct vfsconf *foo)
+{
+
+ if (udf_zone_trans != NULL) {
+ uma_zdestroy(udf_zone_trans);
+ udf_zone_trans = NULL;
+ }
+
+ if (udf_zone_node != NULL) {
+ uma_zdestroy(udf_zone_node);
+ udf_zone_node = NULL;
+ }
+
+ if (udf_zone_ds != NULL) {
+ uma_zdestroy(udf_zone_ds);
+ udf_zone_ds = NULL;
+ }
+
+ return (0);
+}
+
+static int
+udf_mount(struct mount *mp)
+{
+ struct vnode *devvp; /* vnode of the mount device */
+ struct thread *td;
+ struct udf_mnt *imp = NULL;
+ struct vfsoptlist *opts;
+ char *fspec, *cs_disk, *cs_local;
+ int error, len, *udf_flags;
+ struct nameidata nd, *ndp = &nd;
+
+ td = curthread;
+ opts = mp->mnt_optnew;
+
+ /*
+ * Unconditionally mount as read-only.
+ */
+ MNT_ILOCK(mp);
+ mp->mnt_flag |= MNT_RDONLY;
+ MNT_IUNLOCK(mp);
+
+ /*
+ * No root filesystem support. Probably not a big deal, since the
+ * bootloader doesn't understand UDF.
+ */
+ if (mp->mnt_flag & MNT_ROOTFS)
+ return (ENOTSUP);
+
+ fspec = NULL;
+ error = vfs_getopt(opts, "from", (void **)&fspec, &len);
+ if (!error && fspec[len - 1] != '\0')
+ return (EINVAL);
+
+ if (mp->mnt_flag & MNT_UPDATE) {
+ return (0);
+ }
+
+ /* Check that the mount device exists */
+ if (fspec == NULL)
+ return (EINVAL);
+ NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td);
+ if ((error = namei(ndp)))
+ return (error);
+ NDFREE(ndp, NDF_ONLY_PNBUF);
+ devvp = ndp->ni_vp;
+
+ if (vn_isdisk(devvp, &error) == 0) {
+ vput(devvp);
+ return (error);
+ }
+
+ /* Check the access rights on the mount device */
+ error = VOP_ACCESS(devvp, VREAD, td->td_ucred, td);
+ if (error)
+ error = priv_check(td, PRIV_VFS_MOUNT_PERM);
+ if (error) {
+ vput(devvp);
+ return (error);
+ }
+
+ if ((error = udf_mountfs(devvp, mp))) {
+ vrele(devvp);
+ return (error);
+ }
+
+ imp = VFSTOUDFFS(mp);
+
+ udf_flags = NULL;
+ error = vfs_getopt(opts, "flags", (void **)&udf_flags, &len);
+ if (error || len != sizeof(int))
+ return (EINVAL);
+ imp->im_flags = *udf_flags;
+
+ if (imp->im_flags & UDFMNT_KICONV && udf_iconv) {
+ cs_disk = NULL;
+ error = vfs_getopt(opts, "cs_disk", (void **)&cs_disk, &len);
+ if (!error && cs_disk[len - 1] != '\0')
+ return (EINVAL);
+ cs_local = NULL;
+ error = vfs_getopt(opts, "cs_local", (void **)&cs_local, &len);
+ if (!error && cs_local[len - 1] != '\0')
+ return (EINVAL);
+ udf_iconv->open(cs_local, cs_disk, &imp->im_d2l);
+#if 0
+ udf_iconv->open(cs_disk, cs_local, &imp->im_l2d);
+#endif
+ }
+
+ vfs_mountedfrom(mp, fspec);
+ return 0;
+};
+
+/*
+ * Check the descriptor tag for both the correct id and correct checksum.
+ * Return zero if all is good, EINVAL if not.
+ */
+int
+udf_checktag(struct desc_tag *tag, uint16_t id)
+{
+ uint8_t *itag;
+ uint8_t i, cksum = 0;
+
+ itag = (uint8_t *)tag;
+
+ if (le16toh(tag->id) != id)
+ return (EINVAL);
+
+ for (i = 0; i < 16; i++)
+ cksum = cksum + itag[i];
+ cksum = cksum - itag[4];
+
+ if (cksum == tag->cksum)
+ return (0);
+
+ return (EINVAL);
+}
+
+static int
+udf_mountfs(struct vnode *devvp, struct mount *mp)
+{
+ struct buf *bp = NULL;
+ struct cdev *dev;
+ struct anchor_vdp avdp;
+ struct udf_mnt *udfmp = NULL;
+ struct part_desc *pd;
+ struct logvol_desc *lvd;
+ struct fileset_desc *fsd;
+ struct file_entry *root_fentry;
+ uint32_t sector, size, mvds_start, mvds_end;
+ uint32_t logical_secsize;
+ uint32_t fsd_offset = 0;
+ uint16_t part_num = 0, fsd_part = 0;
+ int error = EINVAL;
+ int logvol_found = 0, part_found = 0, fsd_found = 0;
+ int bsize;
+ struct g_consumer *cp;
+ struct bufobj *bo;
+
+ dev = devvp->v_rdev;
+ dev_ref(dev);
+ g_topology_lock();
+ error = g_vfs_open(devvp, &cp, "udf", 0);
+ g_topology_unlock();
+ VOP_UNLOCK(devvp, 0);
+ if (error)
+ goto bail;
+
+ bo = &devvp->v_bufobj;
+
+ 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;
+
+ /* XXX: should be M_WAITOK */
+ udfmp = malloc(sizeof(struct udf_mnt), M_UDFMOUNT,
+ M_NOWAIT | M_ZERO);
+ if (udfmp == NULL) {
+ printf("Cannot allocate UDF mount struct\n");
+ error = ENOMEM;
+ goto bail;
+ }
+
+ mp->mnt_data = udfmp;
+ mp->mnt_stat.f_fsid.val[0] = dev2udev(devvp->v_rdev);
+ mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
+ MNT_ILOCK(mp);
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED;
+ MNT_IUNLOCK(mp);
+ udfmp->im_mountp = mp;
+ udfmp->im_dev = dev;
+ udfmp->im_devvp = devvp;
+ udfmp->im_d2l = NULL;
+ udfmp->im_cp = cp;
+ udfmp->im_bo = bo;
+
+#if 0
+ udfmp->im_l2d = NULL;
+#endif
+ /*
+ * The UDF specification defines a logical sectorsize of 2048
+ * for DVD media.
+ */
+ logical_secsize = 2048;
+
+ if (((logical_secsize % cp->provider->sectorsize) != 0) ||
+ (logical_secsize < cp->provider->sectorsize)) {
+ error = EINVAL;
+ goto bail;
+ }
+
+ bsize = cp->provider->sectorsize;
+
+ /*
+ * Get the Anchor Volume Descriptor Pointer from sector 256.
+ * XXX Should also check sector n - 256, n, and 512.
+ */
+ sector = 256;
+ if ((error = bread(devvp, sector * btodb(logical_secsize), bsize,
+ NOCRED, &bp)) != 0)
+ goto bail;
+ if ((error = udf_checktag((struct desc_tag *)bp->b_data, TAGID_ANCHOR)))
+ goto bail;
+
+ bcopy(bp->b_data, &avdp, sizeof(struct anchor_vdp));
+ brelse(bp);
+ bp = NULL;
+
+ /*
+ * Extract the Partition Descriptor and Logical Volume Descriptor
+ * from the Volume Descriptor Sequence.
+ * XXX Should we care about the partition type right now?
+ * XXX What about multiple partitions?
+ */
+ mvds_start = le32toh(avdp.main_vds_ex.loc);
+ mvds_end = mvds_start + (le32toh(avdp.main_vds_ex.len) - 1) / bsize;
+ for (sector = mvds_start; sector < mvds_end; sector++) {
+ if ((error = bread(devvp, sector * btodb(logical_secsize),
+ bsize, NOCRED, &bp)) != 0) {
+ printf("Can't read sector %d of VDS\n", sector);
+ goto bail;
+ }
+ lvd = (struct logvol_desc *)bp->b_data;
+ if (!udf_checktag(&lvd->tag, TAGID_LOGVOL)) {
+ udfmp->bsize = le32toh(lvd->lb_size);
+ udfmp->bmask = udfmp->bsize - 1;
+ udfmp->bshift = ffs(udfmp->bsize) - 1;
+ fsd_part = le16toh(lvd->_lvd_use.fsd_loc.loc.part_num);
+ fsd_offset = le32toh(lvd->_lvd_use.fsd_loc.loc.lb_num);
+ if (udf_find_partmaps(udfmp, lvd))
+ break;
+ logvol_found = 1;
+ }
+ pd = (struct part_desc *)bp->b_data;
+ if (!udf_checktag(&pd->tag, TAGID_PARTITION)) {
+ part_found = 1;
+ part_num = le16toh(pd->part_num);
+ udfmp->part_len = le32toh(pd->part_len);
+ udfmp->part_start = le32toh(pd->start_loc);
+ }
+
+ brelse(bp);
+ bp = NULL;
+ if ((part_found) && (logvol_found))
+ break;
+ }
+
+ if (!part_found || !logvol_found) {
+ error = EINVAL;
+ goto bail;
+ }
+
+ if (fsd_part != part_num) {
+ printf("FSD does not lie within the partition!\n");
+ error = EINVAL;
+ goto bail;
+ }
+
+
+ /*
+ * Grab the Fileset Descriptor
+ * Thanks to Chuck McCrobie <mccrobie@cablespeed.com> for pointing
+ * me in the right direction here.
+ */
+ sector = udfmp->part_start + fsd_offset;
+ if ((error = RDSECTOR(devvp, sector, udfmp->bsize, &bp)) != 0) {
+ printf("Cannot read sector %d of FSD\n", sector);
+ goto bail;
+ }
+ fsd = (struct fileset_desc *)bp->b_data;
+ if (!udf_checktag(&fsd->tag, TAGID_FSD)) {
+ fsd_found = 1;
+ bcopy(&fsd->rootdir_icb, &udfmp->root_icb,
+ sizeof(struct long_ad));
+ }
+
+ brelse(bp);
+ bp = NULL;
+
+ if (!fsd_found) {
+ printf("Couldn't find the fsd\n");
+ error = EINVAL;
+ goto bail;
+ }
+
+ /*
+ * Find the file entry for the root directory.
+ */
+ sector = le32toh(udfmp->root_icb.loc.lb_num) + udfmp->part_start;
+ size = le32toh(udfmp->root_icb.len);
+ if ((error = udf_readdevblks(udfmp, sector, size, &bp)) != 0) {
+ printf("Cannot read sector %d\n", sector);
+ goto bail;
+ }
+
+ root_fentry = (struct file_entry *)bp->b_data;
+ if ((error = udf_checktag(&root_fentry->tag, TAGID_FENTRY))) {
+ printf("Invalid root file entry!\n");
+ goto bail;
+ }
+
+ brelse(bp);
+ bp = NULL;
+
+ return 0;
+
+bail:
+ if (udfmp != NULL)
+ free(udfmp, M_UDFMOUNT);
+ if (bp != NULL)
+ brelse(bp);
+ if (cp != NULL) {
+ g_topology_lock();
+ g_vfs_close(cp);
+ g_topology_unlock();
+ }
+ dev_rel(dev);
+ return error;
+};
+
+static int
+udf_unmount(struct mount *mp, int mntflags)
+{
+ struct udf_mnt *udfmp;
+ int error, flags = 0;
+
+ udfmp = VFSTOUDFFS(mp);
+
+ if (mntflags & MNT_FORCE)
+ flags |= FORCECLOSE;
+
+ if ((error = vflush(mp, 0, flags, curthread)))
+ return (error);
+
+ if (udfmp->im_flags & UDFMNT_KICONV && udf_iconv) {
+ if (udfmp->im_d2l)
+ udf_iconv->close(udfmp->im_d2l);
+#if 0
+ if (udfmp->im_l2d)
+ udf_iconv->close(udfmp->im_l2d);
+#endif
+ }
+
+ g_topology_lock();
+ g_vfs_close(udfmp->im_cp);
+ g_topology_unlock();
+ vrele(udfmp->im_devvp);
+ dev_rel(udfmp->im_dev);
+
+ if (udfmp->s_table != NULL)
+ free(udfmp->s_table, M_UDFMOUNT);
+
+ free(udfmp, M_UDFMOUNT);
+
+ mp->mnt_data = NULL;
+ MNT_ILOCK(mp);
+ mp->mnt_flag &= ~MNT_LOCAL;
+ MNT_IUNLOCK(mp);
+
+ return (0);
+}
+
+static int
+udf_root(struct mount *mp, int flags, struct vnode **vpp)
+{
+ struct udf_mnt *udfmp;
+ ino_t id;
+
+ udfmp = VFSTOUDFFS(mp);
+
+ id = udf_getid(&udfmp->root_icb);
+
+ return (udf_vget(mp, id, flags, vpp));
+}
+
+static int
+udf_statfs(struct mount *mp, struct statfs *sbp)
+{
+ struct udf_mnt *udfmp;
+
+ udfmp = VFSTOUDFFS(mp);
+
+ sbp->f_bsize = udfmp->bsize;
+ sbp->f_iosize = udfmp->bsize;
+ sbp->f_blocks = udfmp->part_len;
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = 0;
+ sbp->f_ffree = 0;
+ return 0;
+}
+
+int
+udf_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
+{
+ struct buf *bp;
+ struct vnode *devvp;
+ struct udf_mnt *udfmp;
+ struct thread *td;
+ struct vnode *vp;
+ struct udf_node *unode;
+ struct file_entry *fe;
+ int error, sector, size;
+
+ error = vfs_hash_get(mp, ino, flags, curthread, vpp, NULL, NULL);
+ if (error || *vpp != NULL)
+ return (error);
+
+ /*
+ * We must promote to an exclusive lock for vnode creation. This
+ * can happen if lookup is passed LOCKSHARED.
+ */
+ if ((flags & LK_TYPE_MASK) == LK_SHARED) {
+ flags &= ~LK_TYPE_MASK;
+ flags |= LK_EXCLUSIVE;
+ }
+
+ /*
+ * We do not lock vnode creation as it is believed to be too
+ * expensive for such rare case as simultaneous creation of vnode
+ * for same ino by different processes. We just allow them to race
+ * and check later to decide who wins. Let the race begin!
+ */
+
+ td = curthread;
+ udfmp = VFSTOUDFFS(mp);
+
+ unode = uma_zalloc(udf_zone_node, M_WAITOK | M_ZERO);
+
+ if ((error = udf_allocv(mp, &vp, td))) {
+ printf("Error from udf_allocv\n");
+ uma_zfree(udf_zone_node, unode);
+ return (error);
+ }
+
+ unode->i_vnode = vp;
+ unode->hash_id = ino;
+ unode->udfmp = udfmp;
+ vp->v_data = unode;
+
+ lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL);
+ error = insmntque(vp, mp);
+ if (error != 0) {
+ uma_zfree(udf_zone_node, unode);
+ return (error);
+ }
+ error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL);
+ if (error || *vpp != NULL)
+ return (error);
+
+ /*
+ * Copy in the file entry. Per the spec, the size can only be 1 block.
+ */
+ sector = ino + udfmp->part_start;
+ devvp = udfmp->im_devvp;
+ if ((error = RDSECTOR(devvp, sector, udfmp->bsize, &bp)) != 0) {
+ printf("Cannot read sector %d\n", sector);
+ vgone(vp);
+ vput(vp);
+ brelse(bp);
+ *vpp = NULL;
+ return (error);
+ }
+
+ fe = (struct file_entry *)bp->b_data;
+ if (udf_checktag(&fe->tag, TAGID_FENTRY)) {
+ printf("Invalid file entry!\n");
+ vgone(vp);
+ vput(vp);
+ brelse(bp);
+ *vpp = NULL;
+ return (ENOMEM);
+ }
+ size = UDF_FENTRY_SIZE + le32toh(fe->l_ea) + le32toh(fe->l_ad);
+ unode->fentry = malloc(size, M_UDFFENTRY, M_NOWAIT | M_ZERO);
+ if (unode->fentry == NULL) {
+ printf("Cannot allocate file entry block\n");
+ vgone(vp);
+ vput(vp);
+ brelse(bp);
+ *vpp = NULL;
+ return (ENOMEM);
+ }
+
+ bcopy(bp->b_data, unode->fentry, size);
+
+ brelse(bp);
+ bp = NULL;
+
+ switch (unode->fentry->icbtag.file_type) {
+ default:
+ vp->v_type = VBAD;
+ break;
+ case 4:
+ vp->v_type = VDIR;
+ break;
+ case 5:
+ vp->v_type = VREG;
+ break;
+ case 6:
+ vp->v_type = VBLK;
+ break;
+ case 7:
+ vp->v_type = VCHR;
+ break;
+ case 9:
+ vp->v_type = VFIFO;
+ vp->v_op = &udf_fifoops;
+ break;
+ case 10:
+ vp->v_type = VSOCK;
+ break;
+ case 12:
+ vp->v_type = VLNK;
+ break;
+ }
+
+ if (vp->v_type != VFIFO)
+ VN_LOCK_ASHARE(vp);
+
+ if (ino == udf_getid(&udfmp->root_icb))
+ vp->v_vflag |= VV_ROOT;
+
+ *vpp = vp;
+
+ return (0);
+}
+
+static int
+udf_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
+{
+ struct ifid *ifhp;
+ struct vnode *nvp;
+ struct udf_node *np;
+ off_t fsize;
+ int error;
+
+ ifhp = (struct ifid *)fhp;
+
+ if ((error = VFS_VGET(mp, ifhp->ifid_ino, LK_EXCLUSIVE, &nvp)) != 0) {
+ *vpp = NULLVP;
+ return (error);
+ }
+
+ np = VTON(nvp);
+ fsize = le64toh(np->fentry->inf_len);
+
+ *vpp = nvp;
+ vnode_create_vobject(*vpp, fsize, curthread);
+ return (0);
+}
+
+static int
+udf_find_partmaps(struct udf_mnt *udfmp, struct logvol_desc *lvd)
+{
+ struct part_map_spare *pms;
+ struct regid *pmap_id;
+ struct buf *bp;
+ unsigned char regid_id[UDF_REGID_ID_SIZE + 1];
+ int i, k, ptype, psize, error;
+ uint8_t *pmap = (uint8_t *) &lvd->maps[0];
+
+ for (i = 0; i < le32toh(lvd->n_pm); i++) {
+ ptype = pmap[0];
+ psize = pmap[1];
+ if (((ptype != 1) && (ptype != 2)) ||
+ ((psize != UDF_PMAP_TYPE1_SIZE) &&
+ (psize != UDF_PMAP_TYPE2_SIZE))) {
+ printf("Invalid partition map found\n");
+ return (1);
+ }
+
+ if (ptype == 1) {
+ /* Type 1 map. We don't care */
+ pmap += UDF_PMAP_TYPE1_SIZE;
+ continue;
+ }
+
+ /* Type 2 map. Gotta find out the details */
+ pmap_id = (struct regid *)&pmap[4];
+ bzero(&regid_id[0], UDF_REGID_ID_SIZE);
+ bcopy(&pmap_id->id[0], &regid_id[0], UDF_REGID_ID_SIZE);
+
+ if (bcmp(&regid_id[0], "*UDF Sparable Partition",
+ UDF_REGID_ID_SIZE)) {
+ printf("Unsupported partition map: %s\n", &regid_id[0]);
+ return (1);
+ }
+
+ pms = (struct part_map_spare *)pmap;
+ pmap += UDF_PMAP_TYPE2_SIZE;
+ udfmp->s_table = malloc(le32toh(pms->st_size),
+ M_UDFMOUNT, M_NOWAIT | M_ZERO);
+ if (udfmp->s_table == NULL)
+ return (ENOMEM);
+
+ /* Calculate the number of sectors per packet. */
+ /* XXX Logical or physical? */
+ udfmp->p_sectors = le16toh(pms->packet_len) / udfmp->bsize;
+
+ /*
+ * XXX If reading the first Sparing Table fails, should look
+ * for another table.
+ */
+ if ((error = udf_readdevblks(udfmp, le32toh(pms->st_loc[0]),
+ le32toh(pms->st_size), &bp)) != 0) {
+ if (bp != NULL)
+ brelse(bp);
+ printf("Failed to read Sparing Table at sector %d\n",
+ le32toh(pms->st_loc[0]));
+ free(udfmp->s_table, M_UDFMOUNT);
+ return (error);
+ }
+ bcopy(bp->b_data, udfmp->s_table, le32toh(pms->st_size));
+ brelse(bp);
+
+ if (udf_checktag(&udfmp->s_table->tag, 0)) {
+ printf("Invalid sparing table found\n");
+ free(udfmp->s_table, M_UDFMOUNT);
+ return (EINVAL);
+ }
+
+ /* See how many valid entries there are here. The list is
+ * supposed to be sorted. 0xfffffff0 and higher are not valid
+ */
+ for (k = 0; k < le16toh(udfmp->s_table->rt_l); k++) {
+ udfmp->s_table_entries = k;
+ if (le32toh(udfmp->s_table->entries[k].org) >=
+ 0xfffffff0)
+ break;
+ }
+ }
+
+ return (0);
+}
diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c
new file mode 100644
index 000000000000..b1b004f95166
--- /dev/null
+++ b/sys/fs/udf/udf_vnops.c
@@ -0,0 +1,1496 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* udf_vnops.c */
+/* Take care of the vnode side of things */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/stat.h>
+#include <sys/bio.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/iconv.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/dirent.h>
+#include <sys/queue.h>
+#include <sys/unistd.h>
+#include <sys/endian.h>
+
+#include <vm/uma.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/osta.h>
+#include <fs/udf/udf.h>
+#include <fs/udf/udf_mount.h>
+
+extern struct iconv_functions *udf_iconv;
+
+static vop_access_t udf_access;
+static vop_getattr_t udf_getattr;
+static vop_open_t udf_open;
+static vop_ioctl_t udf_ioctl;
+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_setattr_t udf_setattr;
+static vop_strategy_t udf_strategy;
+static vop_bmap_t udf_bmap;
+static vop_cachedlookup_t udf_lookup;
+static vop_reclaim_t udf_reclaim;
+static vop_vptofh_t udf_vptofh;
+static int udf_readatoffset(struct udf_node *node, int *size, off_t offset,
+ struct buf **bp, uint8_t **data);
+static int udf_bmap_internal(struct udf_node *node, off_t offset,
+ daddr_t *sector, uint32_t *max_size);
+
+static struct vop_vector udf_vnodeops = {
+ .vop_default = &default_vnodeops,
+
+ .vop_access = udf_access,
+ .vop_bmap = udf_bmap,
+ .vop_cachedlookup = udf_lookup,
+ .vop_getattr = udf_getattr,
+ .vop_ioctl = udf_ioctl,
+ .vop_lookup = vfs_cache_lookup,
+ .vop_open = udf_open,
+ .vop_pathconf = udf_pathconf,
+ .vop_print = udf_print,
+ .vop_read = udf_read,
+ .vop_readdir = udf_readdir,
+ .vop_readlink = udf_readlink,
+ .vop_reclaim = udf_reclaim,
+ .vop_setattr = udf_setattr,
+ .vop_strategy = udf_strategy,
+ .vop_vptofh = udf_vptofh,
+};
+
+struct vop_vector udf_fifoops = {
+ .vop_default = &fifo_specops,
+ .vop_access = udf_access,
+ .vop_getattr = udf_getattr,
+ .vop_pathconf = udf_pathconf,
+ .vop_print = udf_print,
+ .vop_reclaim = udf_reclaim,
+ .vop_setattr = udf_setattr,
+ .vop_vptofh = udf_vptofh,
+};
+
+static MALLOC_DEFINE(M_UDFFID, "udf_fid", "UDF FileId structure");
+static MALLOC_DEFINE(M_UDFDS, "udf_ds", "UDF Dirstream structure");
+
+#define UDF_INVALID_BMAP -1
+
+int
+udf_allocv(struct mount *mp, struct vnode **vpp, struct thread *td)
+{
+ int error;
+ struct vnode *vp;
+
+ error = getnewvnode("udf", mp, &udf_vnodeops, &vp);
+ if (error) {
+ printf("udf_allocv: failed to allocate new vnode\n");
+ return (error);
+ }
+
+ *vpp = vp;
+ return (0);
+}
+
+/* Convert file entry permission (5 bits per owner/group/user) to a mode_t */
+static mode_t
+udf_permtomode(struct udf_node *node)
+{
+ uint32_t perm;
+ uint16_t flags;
+ mode_t mode;
+
+ perm = le32toh(node->fentry->perm);
+ flags = le16toh(node->fentry->icbtag.flags);
+
+ mode = perm & UDF_FENTRY_PERM_USER_MASK;
+ mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK) >> 2);
+ mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4);
+ mode |= ((flags & UDF_ICB_TAG_FLAGS_STICKY) << 4);
+ mode |= ((flags & UDF_ICB_TAG_FLAGS_SETGID) << 6);
+ mode |= ((flags & UDF_ICB_TAG_FLAGS_SETUID) << 8);
+
+ return (mode);
+}
+
+static int
+udf_access(struct vop_access_args *a)
+{
+ struct vnode *vp;
+ struct udf_node *node;
+ accmode_t accmode;
+ mode_t mode;
+
+ vp = a->a_vp;
+ node = VTON(vp);
+ accmode = a->a_accmode;
+
+ if (accmode & VWRITE) {
+ switch (vp->v_type) {
+ case VDIR:
+ case VLNK:
+ case VREG:
+ return (EROFS);
+ /* NOT REACHED */
+ default:
+ break;
+ }
+ }
+
+ mode = udf_permtomode(node);
+
+ return (vaccess(vp->v_type, mode, node->fentry->uid, node->fentry->gid,
+ accmode, a->a_cred, NULL));
+}
+
+static int
+udf_open(struct vop_open_args *ap) {
+ struct udf_node *np = VTON(ap->a_vp);
+ off_t fsize;
+
+ fsize = le64toh(np->fentry->inf_len);
+ vnode_create_vobject(ap->a_vp, fsize, ap->a_td);
+ return 0;
+}
+
+static const int mon_lens[2][12] = {
+ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
+ {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
+};
+
+static int
+udf_isaleapyear(int year)
+{
+ int i;
+
+ i = (year % 4) ? 0 : 1;
+ i &= (year % 100) ? 1 : 0;
+ i |= (year % 400) ? 0 : 1;
+
+ return i;
+}
+
+/*
+ * Timezone calculation compliments of Julian Elischer <julian@elischer.org>.
+ */
+static void
+udf_timetotimespec(struct timestamp *time, struct timespec *t)
+{
+ int i, lpyear, daysinyear, year, startyear;
+ union {
+ uint16_t u_tz_offset;
+ int16_t s_tz_offset;
+ } tz;
+
+ /*
+ * DirectCD seems to like using bogus year values.
+ * Don't trust time->month as it will be used for an array index.
+ */
+ year = le16toh(time->year);
+ if (year < 1970 || time->month < 1 || time->month > 12) {
+ t->tv_sec = 0;
+ t->tv_nsec = 0;
+ return;
+ }
+
+ /* Calculate the time and day */
+ t->tv_sec = time->second;
+ t->tv_sec += time->minute * 60;
+ t->tv_sec += time->hour * 3600;
+ t->tv_sec += (time->day - 1) * 3600 * 24;
+
+ /* Calculate the month */
+ lpyear = udf_isaleapyear(year);
+ t->tv_sec += mon_lens[lpyear][time->month - 1] * 3600 * 24;
+
+ /* Speed up the calculation */
+ startyear = 1970;
+ if (year > 2009) {
+ t->tv_sec += 1262304000;
+ startyear += 40;
+ } else if (year > 1999) {
+ t->tv_sec += 946684800;
+ startyear += 30;
+ } else if (year > 1989) {
+ t->tv_sec += 631152000;
+ startyear += 20;
+ } else if (year > 1979) {
+ t->tv_sec += 315532800;
+ startyear += 10;
+ }
+
+ daysinyear = (year - startyear) * 365;
+ for (i = startyear; i < year; i++)
+ daysinyear += udf_isaleapyear(i);
+ t->tv_sec += daysinyear * 3600 * 24;
+
+ /* Calculate microseconds */
+ t->tv_nsec = time->centisec * 10000 + time->hund_usec * 100 +
+ time->usec;
+
+ /*
+ * Calculate the time zone. The timezone is 12 bit signed 2's
+ * complement, so we gotta do some extra magic to handle it right.
+ */
+ tz.u_tz_offset = le16toh(time->type_tz);
+ tz.u_tz_offset &= 0x0fff;
+ if (tz.u_tz_offset & 0x0800)
+ tz.u_tz_offset |= 0xf000; /* extend the sign to 16 bits */
+ if ((le16toh(time->type_tz) & 0x1000) && (tz.s_tz_offset != -2047))
+ t->tv_sec -= tz.s_tz_offset * 60;
+
+ return;
+}
+
+static int
+udf_getattr(struct vop_getattr_args *a)
+{
+ struct vnode *vp;
+ struct udf_node *node;
+ struct vattr *vap;
+ struct file_entry *fentry;
+ struct timespec ts;
+
+ ts.tv_sec = 0;
+
+ vp = a->a_vp;
+ vap = a->a_vap;
+ node = VTON(vp);
+ fentry = node->fentry;
+
+ vap->va_fsid = dev2udev(node->udfmp->im_dev);
+ vap->va_fileid = node->hash_id;
+ vap->va_mode = udf_permtomode(node);
+ vap->va_nlink = le16toh(fentry->link_cnt);
+ /*
+ * XXX The spec says that -1 is valid for uid/gid and indicates an
+ * invalid uid/gid. How should this be represented?
+ */
+ vap->va_uid = (le32toh(fentry->uid) == -1) ? 0 : le32toh(fentry->uid);
+ vap->va_gid = (le32toh(fentry->gid) == -1) ? 0 : le32toh(fentry->gid);
+ udf_timetotimespec(&fentry->atime, &vap->va_atime);
+ udf_timetotimespec(&fentry->mtime, &vap->va_mtime);
+ vap->va_ctime = vap->va_mtime; /* XXX Stored as an Extended Attribute */
+ vap->va_rdev = NODEV;
+ if (vp->v_type & VDIR) {
+ /*
+ * Directories that are recorded within their ICB will show
+ * as having 0 blocks recorded. Since tradition dictates
+ * that directories consume at least one logical block,
+ * make it appear so.
+ */
+ if (fentry->logblks_rec != 0) {
+ vap->va_size =
+ le64toh(fentry->logblks_rec) * node->udfmp->bsize;
+ } else {
+ vap->va_size = node->udfmp->bsize;
+ }
+ } else {
+ vap->va_size = le64toh(fentry->inf_len);
+ }
+ vap->va_flags = 0;
+ vap->va_gen = 1;
+ vap->va_blocksize = node->udfmp->bsize;
+ vap->va_bytes = le64toh(fentry->inf_len);
+ vap->va_type = vp->v_type;
+ vap->va_filerev = 0; /* XXX */
+ return (0);
+}
+
+static int
+udf_setattr(struct vop_setattr_args *a)
+{
+ struct vnode *vp;
+ struct vattr *vap;
+
+ vp = a->a_vp;
+ vap = a->a_vap;
+ if (vap->va_flags != (u_long)VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
+ vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
+ vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL)
+ return (EROFS);
+ if (vap->va_size != (u_quad_t)VNOVAL) {
+ switch (vp->v_type) {
+ case VDIR:
+ return (EISDIR);
+ case VLNK:
+ case VREG:
+ return (EROFS);
+ case VCHR:
+ case VBLK:
+ case VSOCK:
+ case VFIFO:
+ case VNON:
+ case VBAD:
+ case VMARKER:
+ return (0);
+ }
+ }
+ return (0);
+}
+
+/*
+ * File specific ioctls.
+ */
+static int
+udf_ioctl(struct vop_ioctl_args *a)
+{
+ printf("%s called\n", __func__);
+ return (ENOTTY);
+}
+
+/*
+ * I'm not sure that this has much value in a read-only filesystem, but
+ * cd9660 has it too.
+ */
+static int
+udf_pathconf(struct vop_pathconf_args *a)
+{
+
+ switch (a->a_name) {
+ case _PC_FILESIZEBITS:
+ *a->a_retval = 64;
+ return (0);
+ case _PC_LINK_MAX:
+ *a->a_retval = 65535;
+ return (0);
+ case _PC_NAME_MAX:
+ *a->a_retval = NAME_MAX;
+ return (0);
+ case _PC_SYMLINK_MAX:
+ *a->a_retval = MAXPATHLEN;
+ return (0);
+ case _PC_NO_TRUNC:
+ *a->a_retval = 1;
+ return (0);
+ case _PC_PIPE_BUF:
+ if (a->a_vp->v_type == VDIR || a->a_vp->v_type == VFIFO) {
+ *a->a_retval = PIPE_BUF;
+ return (0);
+ }
+ return (EINVAL);
+ default:
+ return (vop_stdpathconf(a));
+ }
+}
+
+static int
+udf_print(struct vop_print_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct udf_node *node = VTON(vp);
+
+ printf(" ino %lu, on dev %s", (u_long)node->hash_id,
+ devtoname(node->udfmp->im_dev));
+ if (vp->v_type == VFIFO)
+ fifo_printinfo(vp);
+ printf("\n");
+ return (0);
+}
+
+#define lblkno(udfmp, loc) ((loc) >> (udfmp)->bshift)
+#define blkoff(udfmp, loc) ((loc) & (udfmp)->bmask)
+#define lblktosize(udfmp, blk) ((blk) << (udfmp)->bshift)
+
+static inline int
+is_data_in_fentry(const struct udf_node *node)
+{
+ const struct file_entry *fentry = node->fentry;
+
+ return ((le16toh(fentry->icbtag.flags) & 0x7) == 3);
+}
+
+static int
+udf_read(struct vop_read_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct udf_node *node = VTON(vp);
+ struct udf_mnt *udfmp;
+ struct file_entry *fentry;
+ struct buf *bp;
+ uint8_t *data;
+ daddr_t lbn, rablock;
+ off_t diff, fsize;
+ ssize_t n;
+ int error = 0;
+ long size, on;
+
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+
+ if (is_data_in_fentry(node)) {
+ fentry = node->fentry;
+ data = &fentry->data[le32toh(fentry->l_ea)];
+ fsize = le32toh(fentry->l_ad);
+
+ n = uio->uio_resid;
+ diff = fsize - uio->uio_offset;
+ if (diff <= 0)
+ return (0);
+ if (diff < n)
+ n = diff;
+ error = uiomove(data + uio->uio_offset, (int)n, uio);
+ return (error);
+ }
+
+ fsize = le64toh(node->fentry->inf_len);
+ udfmp = node->udfmp;
+ do {
+ lbn = lblkno(udfmp, uio->uio_offset);
+ on = blkoff(udfmp, uio->uio_offset);
+ n = min((u_int)(udfmp->bsize - on),
+ uio->uio_resid);
+ diff = fsize - uio->uio_offset;
+ if (diff <= 0)
+ return (0);
+ if (diff < n)
+ n = diff;
+ size = udfmp->bsize;
+ rablock = lbn + 1;
+ if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) {
+ if (lblktosize(udfmp, rablock) < fsize) {
+ error = cluster_read(vp, fsize, lbn, size,
+ NOCRED, uio->uio_resid,
+ (ap->a_ioflag >> 16), 0, &bp);
+ } else {
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ }
+ } else {
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ }
+ if (error != 0) {
+ brelse(bp);
+ return (error);
+ }
+ n = min(n, size - bp->b_resid);
+
+ error = uiomove(bp->b_data + on, (int)n, uio);
+ brelse(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return (error);
+}
+
+/*
+ * Call the OSTA routines to translate the name from a CS0 dstring to a
+ * 16-bit Unicode String. Hooks need to be placed in here to translate from
+ * Unicode to the encoding that the kernel/user expects. Return the length
+ * of the translated string.
+ */
+static int
+udf_transname(char *cs0string, char *destname, int len, struct udf_mnt *udfmp)
+{
+ unicode_t *transname;
+ char *unibuf, *unip;
+ int i, destlen;
+ ssize_t unilen = 0;
+ size_t destleft = MAXNAMLEN;
+
+ /* Convert 16-bit Unicode to destname */
+ if (udfmp->im_flags & UDFMNT_KICONV && udf_iconv) {
+ /* allocate a buffer big enough to hold an 8->16 bit expansion */
+ unibuf = uma_zalloc(udf_zone_trans, M_WAITOK);
+ unip = unibuf;
+ if ((unilen = (ssize_t)udf_UncompressUnicodeByte(len, cs0string, unibuf)) == -1) {
+ printf("udf: Unicode translation failed\n");
+ uma_zfree(udf_zone_trans, unibuf);
+ return 0;
+ }
+
+ while (unilen > 0 && destleft > 0) {
+ udf_iconv->conv(udfmp->im_d2l, __DECONST(const char **,
+ &unibuf), (size_t *)&unilen, (char **)&destname,
+ &destleft);
+ /* Unconverted character found */
+ if (unilen > 0 && destleft > 0) {
+ *destname++ = '?';
+ destleft--;
+ unibuf += 2;
+ unilen -= 2;
+ }
+ }
+ uma_zfree(udf_zone_trans, unip);
+ *destname = '\0';
+ destlen = MAXNAMLEN - (int)destleft;
+ } else {
+ /* allocate a buffer big enough to hold an 8->16 bit expansion */
+ transname = uma_zalloc(udf_zone_trans, M_WAITOK);
+
+ if ((unilen = (ssize_t)udf_UncompressUnicode(len, cs0string, transname)) == -1) {
+ printf("udf: Unicode translation failed\n");
+ uma_zfree(udf_zone_trans, transname);
+ return 0;
+ }
+
+ for (i = 0; i < unilen ; i++) {
+ if (transname[i] & 0xff00) {
+ destname[i] = '.'; /* Fudge the 16bit chars */
+ } else {
+ destname[i] = transname[i] & 0xff;
+ }
+ }
+ uma_zfree(udf_zone_trans, transname);
+ destname[unilen] = 0;
+ destlen = (int)unilen;
+ }
+
+ return (destlen);
+}
+
+/*
+ * Compare a CS0 dstring with a name passed in from the VFS layer. Return
+ * 0 on a successful match, nonzero otherwise. Unicode work may need to be done
+ * here also.
+ */
+static int
+udf_cmpname(char *cs0string, char *cmpname, int cs0len, int cmplen, struct udf_mnt *udfmp)
+{
+ char *transname;
+ int error = 0;
+
+ /* This is overkill, but not worth creating a new zone */
+ transname = uma_zalloc(udf_zone_trans, M_WAITOK);
+
+ cs0len = udf_transname(cs0string, transname, cs0len, udfmp);
+
+ /* Easy check. If they aren't the same length, they aren't equal */
+ if ((cs0len == 0) || (cs0len != cmplen))
+ error = -1;
+ else
+ error = bcmp(transname, cmpname, cmplen);
+
+ uma_zfree(udf_zone_trans, transname);
+ return (error);
+}
+
+struct udf_uiodir {
+ struct dirent *dirent;
+ u_long *cookies;
+ int ncookies;
+ int acookies;
+ int eofflag;
+};
+
+static int
+udf_uiodir(struct udf_uiodir *uiodir, int de_size, struct uio *uio, long cookie)
+{
+ if (uiodir->cookies != NULL) {
+ if (++uiodir->acookies > uiodir->ncookies) {
+ uiodir->eofflag = 0;
+ return (-1);
+ }
+ *uiodir->cookies++ = cookie;
+ }
+
+ if (uio->uio_resid < de_size) {
+ uiodir->eofflag = 0;
+ return (-1);
+ }
+
+ return (uiomove(uiodir->dirent, de_size, uio));
+}
+
+static struct udf_dirstream *
+udf_opendir(struct udf_node *node, int offset, int fsize, struct udf_mnt *udfmp)
+{
+ struct udf_dirstream *ds;
+
+ ds = uma_zalloc(udf_zone_ds, M_WAITOK | M_ZERO);
+
+ ds->node = node;
+ ds->offset = offset;
+ ds->udfmp = udfmp;
+ ds->fsize = fsize;
+
+ return (ds);
+}
+
+static struct fileid_desc *
+udf_getfid(struct udf_dirstream *ds)
+{
+ struct fileid_desc *fid;
+ int error, frag_size = 0, total_fid_size;
+
+ /* End of directory? */
+ if (ds->offset + ds->off >= ds->fsize) {
+ ds->error = 0;
+ return (NULL);
+ }
+
+ /* Grab the first extent of the directory */
+ if (ds->off == 0) {
+ ds->size = 0;
+ error = udf_readatoffset(ds->node, &ds->size, ds->offset,
+ &ds->bp, &ds->data);
+ if (error) {
+ ds->error = error;
+ if (ds->bp != NULL)
+ brelse(ds->bp);
+ return (NULL);
+ }
+ }
+
+ /*
+ * Clean up from a previous fragmented FID.
+ * XXX Is this the right place for this?
+ */
+ if (ds->fid_fragment && ds->buf != NULL) {
+ ds->fid_fragment = 0;
+ free(ds->buf, M_UDFFID);
+ }
+
+ fid = (struct fileid_desc*)&ds->data[ds->off];
+
+ /*
+ * Check to see if the fid is fragmented. The first test
+ * ensures that we don't wander off the end of the buffer
+ * looking for the l_iu and l_fi fields.
+ */
+ if (ds->off + UDF_FID_SIZE > ds->size ||
+ ds->off + le16toh(fid->l_iu) + fid->l_fi + UDF_FID_SIZE > ds->size){
+
+ /* Copy what we have of the fid into a buffer */
+ frag_size = ds->size - ds->off;
+ if (frag_size >= ds->udfmp->bsize) {
+ printf("udf: invalid FID fragment\n");
+ ds->error = EINVAL;
+ return (NULL);
+ }
+
+ /*
+ * File ID descriptors can only be at most one
+ * logical sector in size.
+ */
+ ds->buf = malloc(ds->udfmp->bsize, M_UDFFID,
+ M_WAITOK | M_ZERO);
+ bcopy(fid, ds->buf, frag_size);
+
+ /* Reduce all of the casting magic */
+ fid = (struct fileid_desc*)ds->buf;
+
+ if (ds->bp != NULL)
+ brelse(ds->bp);
+
+ /* Fetch the next allocation */
+ ds->offset += ds->size;
+ ds->size = 0;
+ error = udf_readatoffset(ds->node, &ds->size, ds->offset,
+ &ds->bp, &ds->data);
+ if (error) {
+ ds->error = error;
+ return (NULL);
+ }
+
+ /*
+ * If the fragment was so small that we didn't get
+ * the l_iu and l_fi fields, copy those in.
+ */
+ if (frag_size < UDF_FID_SIZE)
+ bcopy(ds->data, &ds->buf[frag_size],
+ UDF_FID_SIZE - frag_size);
+
+ /*
+ * Now that we have enough of the fid to work with,
+ * copy in the rest of the fid from the new
+ * allocation.
+ */
+ total_fid_size = UDF_FID_SIZE + le16toh(fid->l_iu) + fid->l_fi;
+ if (total_fid_size > ds->udfmp->bsize) {
+ printf("udf: invalid FID\n");
+ ds->error = EIO;
+ return (NULL);
+ }
+ bcopy(ds->data, &ds->buf[frag_size],
+ total_fid_size - frag_size);
+
+ ds->fid_fragment = 1;
+ } else {
+ total_fid_size = le16toh(fid->l_iu) + fid->l_fi + UDF_FID_SIZE;
+ }
+
+ /*
+ * Update the offset. Align on a 4 byte boundary because the
+ * UDF spec says so.
+ */
+ ds->this_off = ds->offset + ds->off;
+ if (!ds->fid_fragment) {
+ ds->off += (total_fid_size + 3) & ~0x03;
+ } else {
+ ds->off = (total_fid_size - frag_size + 3) & ~0x03;
+ }
+
+ return (fid);
+}
+
+static void
+udf_closedir(struct udf_dirstream *ds)
+{
+
+ if (ds->bp != NULL)
+ brelse(ds->bp);
+
+ if (ds->fid_fragment && ds->buf != NULL)
+ free(ds->buf, M_UDFFID);
+
+ uma_zfree(udf_zone_ds, ds);
+}
+
+static int
+udf_readdir(struct vop_readdir_args *a)
+{
+ struct vnode *vp;
+ struct uio *uio;
+ struct dirent dir;
+ struct udf_node *node;
+ struct udf_mnt *udfmp;
+ struct fileid_desc *fid;
+ struct udf_uiodir uiodir;
+ struct udf_dirstream *ds;
+ u_long *cookies = NULL;
+ int ncookies;
+ int error = 0;
+
+ vp = a->a_vp;
+ uio = a->a_uio;
+ node = VTON(vp);
+ udfmp = node->udfmp;
+ uiodir.eofflag = 1;
+
+ if (a->a_ncookies != NULL) {
+ /*
+ * Guess how many entries are needed. If we run out, this
+ * function will be called again and thing will pick up were
+ * it left off.
+ */
+ ncookies = uio->uio_resid / 8;
+ cookies = malloc(sizeof(u_long) * ncookies,
+ M_TEMP, M_WAITOK);
+ if (cookies == NULL)
+ return (ENOMEM);
+ uiodir.ncookies = ncookies;
+ uiodir.cookies = cookies;
+ uiodir.acookies = 0;
+ } else {
+ uiodir.cookies = NULL;
+ }
+
+ /*
+ * Iterate through the file id descriptors. Give the parent dir
+ * entry special attention.
+ */
+ ds = udf_opendir(node, uio->uio_offset, le64toh(node->fentry->inf_len),
+ node->udfmp);
+
+ while ((fid = udf_getfid(ds)) != NULL) {
+
+ /* XXX Should we return an error on a bad fid? */
+ if (udf_checktag(&fid->tag, TAGID_FID)) {
+ printf("Invalid FID tag\n");
+ hexdump(fid, UDF_FID_SIZE, NULL, 0);
+ error = EIO;
+ break;
+ }
+
+ /* Is this a deleted file? */
+ if (fid->file_char & UDF_FILE_CHAR_DEL)
+ continue;
+
+ if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
+ /* Do up the '.' and '..' entries. Dummy values are
+ * used for the cookies since the offset here is
+ * usually zero, and NFS doesn't like that value
+ */
+ dir.d_fileno = node->hash_id;
+ dir.d_type = DT_DIR;
+ dir.d_name[0] = '.';
+ dir.d_namlen = 1;
+ dir.d_reclen = GENERIC_DIRSIZ(&dir);
+ dir.d_off = 1;
+ dirent_terminate(&dir);
+ uiodir.dirent = &dir;
+ error = udf_uiodir(&uiodir, dir.d_reclen, uio, 1);
+ if (error)
+ break;
+
+ dir.d_fileno = udf_getid(&fid->icb);
+ dir.d_type = DT_DIR;
+ dir.d_name[0] = '.';
+ dir.d_name[1] = '.';
+ dir.d_namlen = 2;
+ dir.d_reclen = GENERIC_DIRSIZ(&dir);
+ dir.d_off = 2;
+ dirent_terminate(&dir);
+ uiodir.dirent = &dir;
+ error = udf_uiodir(&uiodir, dir.d_reclen, uio, 2);
+ } else {
+ dir.d_namlen = udf_transname(&fid->data[fid->l_iu],
+ &dir.d_name[0], fid->l_fi, udfmp);
+ dir.d_fileno = udf_getid(&fid->icb);
+ dir.d_type = (fid->file_char & UDF_FILE_CHAR_DIR) ?
+ DT_DIR : DT_UNKNOWN;
+ dir.d_reclen = GENERIC_DIRSIZ(&dir);
+ dir.d_off = ds->this_off;
+ dirent_terminate(&dir);
+ uiodir.dirent = &dir;
+ error = udf_uiodir(&uiodir, dir.d_reclen, uio,
+ ds->this_off);
+ }
+ if (error)
+ break;
+ uio->uio_offset = ds->offset + ds->off;
+ }
+
+ /* tell the calling layer whether we need to be called again */
+ *a->a_eofflag = uiodir.eofflag;
+
+ if (error < 0)
+ error = 0;
+ if (!error)
+ error = ds->error;
+
+ udf_closedir(ds);
+
+ if (a->a_ncookies != NULL) {
+ if (error)
+ free(cookies, M_TEMP);
+ else {
+ *a->a_ncookies = uiodir.acookies;
+ *a->a_cookies = cookies;
+ }
+ }
+
+ return (error);
+}
+
+static int
+udf_readlink(struct vop_readlink_args *ap)
+{
+ struct path_component *pc, *end;
+ struct vnode *vp;
+ struct uio uio;
+ struct iovec iov[1];
+ struct udf_node *node;
+ void *buf;
+ char *cp;
+ int error, len, root;
+
+ /*
+ * A symbolic link in UDF is a list of variable-length path
+ * component structures. We build a pathname in the caller's
+ * uio by traversing this list.
+ */
+ vp = ap->a_vp;
+ node = VTON(vp);
+ len = le64toh(node->fentry->inf_len);
+ buf = malloc(len, M_DEVBUF, M_WAITOK);
+ iov[0].iov_len = len;
+ iov[0].iov_base = buf;
+ uio.uio_iov = iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_offset = 0;
+ uio.uio_resid = iov[0].iov_len;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_rw = UIO_READ;
+ uio.uio_td = curthread;
+ error = VOP_READ(vp, &uio, 0, ap->a_cred);
+ if (error)
+ goto error;
+
+ pc = buf;
+ end = (void *)((char *)buf + len);
+ root = 0;
+ while (pc < end) {
+ switch (pc->type) {
+ case UDF_PATH_ROOT:
+ /* Only allow this at the beginning of a path. */
+ if ((void *)pc != buf) {
+ error = EINVAL;
+ goto error;
+ }
+ cp = "/";
+ len = 1;
+ root = 1;
+ break;
+ case UDF_PATH_DOT:
+ cp = ".";
+ len = 1;
+ break;
+ case UDF_PATH_DOTDOT:
+ cp = "..";
+ len = 2;
+ break;
+ case UDF_PATH_PATH:
+ if (pc->length == 0) {
+ error = EINVAL;
+ goto error;
+ }
+ /*
+ * XXX: We only support CS8 which appears to map
+ * to ASCII directly.
+ */
+ switch (pc->identifier[0]) {
+ case 8:
+ cp = pc->identifier + 1;
+ len = pc->length - 1;
+ break;
+ default:
+ error = EOPNOTSUPP;
+ goto error;
+ }
+ break;
+ default:
+ error = EINVAL;
+ goto error;
+ }
+
+ /*
+ * If this is not the first component, insert a path
+ * separator.
+ */
+ if (pc != buf) {
+ /* If we started with root we already have a "/". */
+ if (root)
+ goto skipslash;
+ root = 0;
+ if (ap->a_uio->uio_resid < 1) {
+ error = ENAMETOOLONG;
+ goto error;
+ }
+ error = uiomove("/", 1, ap->a_uio);
+ if (error)
+ break;
+ }
+ skipslash:
+
+ /* Append string at 'cp' of length 'len' to our path. */
+ if (len > ap->a_uio->uio_resid) {
+ error = ENAMETOOLONG;
+ goto error;
+ }
+ error = uiomove(cp, len, ap->a_uio);
+ if (error)
+ break;
+
+ /* Advance to next component. */
+ pc = (void *)((char *)pc + 4 + pc->length);
+ }
+error:
+ free(buf, M_DEVBUF);
+ return (error);
+}
+
+static int
+udf_strategy(struct vop_strategy_args *a)
+{
+ struct buf *bp;
+ struct vnode *vp;
+ struct udf_node *node;
+ struct bufobj *bo;
+ off_t offset;
+ uint32_t maxsize;
+ daddr_t sector;
+ int error;
+
+ bp = a->a_bp;
+ vp = a->a_vp;
+ node = VTON(vp);
+
+ if (bp->b_blkno == bp->b_lblkno) {
+ offset = lblktosize(node->udfmp, bp->b_lblkno);
+ error = udf_bmap_internal(node, offset, &sector, &maxsize);
+ if (error) {
+ clrbuf(bp);
+ bp->b_blkno = -1;
+ bufdone(bp);
+ return (0);
+ }
+ /* bmap gives sector numbers, bio works with device blocks */
+ bp->b_blkno = sector << (node->udfmp->bshift - DEV_BSHIFT);
+ }
+ bo = node->udfmp->im_bo;
+ bp->b_iooffset = dbtob(bp->b_blkno);
+ BO_STRATEGY(bo, bp);
+ return (0);
+}
+
+static int
+udf_bmap(struct vop_bmap_args *a)
+{
+ struct udf_node *node;
+ uint32_t max_size;
+ daddr_t lsector;
+ int nblk;
+ int error;
+
+ node = VTON(a->a_vp);
+
+ if (a->a_bop != NULL)
+ *a->a_bop = &node->udfmp->im_devvp->v_bufobj;
+ if (a->a_bnp == NULL)
+ return (0);
+ if (a->a_runb)
+ *a->a_runb = 0;
+
+ /*
+ * UDF_INVALID_BMAP means data embedded into fentry, this is an internal
+ * error that should not be propagated to calling code.
+ * Most obvious mapping for this error is EOPNOTSUPP as we can not truly
+ * translate block numbers in this case.
+ * Incidentally, this return code will make vnode pager to use VOP_READ
+ * to get data for mmap-ed pages and udf_read knows how to do the right
+ * thing for this kind of files.
+ */
+ error = udf_bmap_internal(node, a->a_bn << node->udfmp->bshift,
+ &lsector, &max_size);
+ if (error == UDF_INVALID_BMAP)
+ return (EOPNOTSUPP);
+ if (error)
+ return (error);
+
+ /* Translate logical to physical sector number */
+ *a->a_bnp = lsector << (node->udfmp->bshift - DEV_BSHIFT);
+
+ /*
+ * Determine maximum number of readahead blocks following the
+ * requested block.
+ */
+ if (a->a_runp) {
+ nblk = (max_size >> node->udfmp->bshift) - 1;
+ if (nblk <= 0)
+ *a->a_runp = 0;
+ else if (nblk >= (MAXBSIZE >> node->udfmp->bshift))
+ *a->a_runp = (MAXBSIZE >> node->udfmp->bshift) - 1;
+ else
+ *a->a_runp = nblk;
+ }
+
+ if (a->a_runb) {
+ *a->a_runb = 0;
+ }
+
+ return (0);
+}
+
+/*
+ * The all powerful VOP_LOOKUP().
+ */
+static int
+udf_lookup(struct vop_cachedlookup_args *a)
+{
+ struct vnode *dvp;
+ struct vnode *tdp = NULL;
+ struct vnode **vpp = a->a_vpp;
+ struct udf_node *node;
+ struct udf_mnt *udfmp;
+ struct fileid_desc *fid = NULL;
+ struct udf_dirstream *ds;
+ u_long nameiop;
+ u_long flags;
+ char *nameptr;
+ long namelen;
+ ino_t id = 0;
+ int offset, error = 0;
+ int fsize, lkflags, ltype, numdirpasses;
+
+ dvp = a->a_dvp;
+ node = VTON(dvp);
+ udfmp = node->udfmp;
+ nameiop = a->a_cnp->cn_nameiop;
+ flags = a->a_cnp->cn_flags;
+ lkflags = a->a_cnp->cn_lkflags;
+ nameptr = a->a_cnp->cn_nameptr;
+ namelen = a->a_cnp->cn_namelen;
+ fsize = le64toh(node->fentry->inf_len);
+
+ /*
+ * If this is a LOOKUP and we've already partially searched through
+ * the directory, pick up where we left off and flag that the
+ * directory may need to be searched twice. For a full description,
+ * see /sys/fs/cd9660/cd9660_lookup.c:cd9660_lookup()
+ */
+ if (nameiop != LOOKUP || node->diroff == 0 || node->diroff > fsize) {
+ offset = 0;
+ numdirpasses = 1;
+ } else {
+ offset = node->diroff;
+ numdirpasses = 2;
+ nchstats.ncs_2passes++;
+ }
+
+lookloop:
+ ds = udf_opendir(node, offset, fsize, udfmp);
+
+ while ((fid = udf_getfid(ds)) != NULL) {
+
+ /* XXX Should we return an error on a bad fid? */
+ if (udf_checktag(&fid->tag, TAGID_FID)) {
+ printf("udf_lookup: Invalid tag\n");
+ error = EIO;
+ break;
+ }
+
+ /* Is this a deleted file? */
+ if (fid->file_char & UDF_FILE_CHAR_DEL)
+ continue;
+
+ if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
+ if (flags & ISDOTDOT) {
+ id = udf_getid(&fid->icb);
+ break;
+ }
+ } else {
+ if (!(udf_cmpname(&fid->data[fid->l_iu],
+ nameptr, fid->l_fi, namelen, udfmp))) {
+ id = udf_getid(&fid->icb);
+ break;
+ }
+ }
+ }
+
+ if (!error)
+ error = ds->error;
+
+ /* XXX Bail out here? */
+ if (error) {
+ udf_closedir(ds);
+ return (error);
+ }
+
+ /* Did we have a match? */
+ if (id) {
+ /*
+ * Remember where this entry was if it's the final
+ * component.
+ */
+ if ((flags & ISLASTCN) && nameiop == LOOKUP)
+ node->diroff = ds->offset + ds->off;
+ if (numdirpasses == 2)
+ nchstats.ncs_pass2++;
+ udf_closedir(ds);
+
+ if (flags & ISDOTDOT) {
+ error = vn_vget_ino(dvp, id, lkflags, &tdp);
+ } else if (node->hash_id == id) {
+ VREF(dvp); /* we want ourself, ie "." */
+ /*
+ * When we lookup "." we still can be asked to lock it
+ * differently.
+ */
+ ltype = lkflags & LK_TYPE_MASK;
+ if (ltype != VOP_ISLOCKED(dvp)) {
+ if (ltype == LK_EXCLUSIVE)
+ vn_lock(dvp, LK_UPGRADE | LK_RETRY);
+ else /* if (ltype == LK_SHARED) */
+ vn_lock(dvp, LK_DOWNGRADE | LK_RETRY);
+ }
+ tdp = dvp;
+ } else
+ error = udf_vget(udfmp->im_mountp, id, lkflags, &tdp);
+ if (!error) {
+ *vpp = tdp;
+ /* Put this entry in the cache */
+ if (flags & MAKEENTRY)
+ cache_enter(dvp, *vpp, a->a_cnp);
+ }
+ } else {
+ /* Name wasn't found on this pass. Do another pass? */
+ if (numdirpasses == 2) {
+ numdirpasses--;
+ offset = 0;
+ udf_closedir(ds);
+ goto lookloop;
+ }
+ udf_closedir(ds);
+
+ /* Enter name into cache as non-existant */
+ if (flags & MAKEENTRY)
+ cache_enter(dvp, *vpp, a->a_cnp);
+
+ if ((flags & ISLASTCN) &&
+ (nameiop == CREATE || nameiop == RENAME)) {
+ error = EROFS;
+ } else {
+ error = ENOENT;
+ }
+ }
+
+ return (error);
+}
+
+static int
+udf_reclaim(struct vop_reclaim_args *a)
+{
+ struct vnode *vp;
+ struct udf_node *unode;
+
+ vp = a->a_vp;
+ unode = VTON(vp);
+
+ /*
+ * Destroy the vm object and flush associated pages.
+ */
+ vnode_destroy_vobject(vp);
+
+ if (unode != NULL) {
+ vfs_hash_remove(vp);
+
+ if (unode->fentry != NULL)
+ free(unode->fentry, M_UDFFENTRY);
+ uma_zfree(udf_zone_node, unode);
+ vp->v_data = NULL;
+ }
+
+ return (0);
+}
+
+static int
+udf_vptofh(struct vop_vptofh_args *a)
+{
+ struct udf_node *node;
+ struct ifid *ifhp;
+
+ node = VTON(a->a_vp);
+ ifhp = (struct ifid *)a->a_fhp;
+ ifhp->ifid_len = sizeof(struct ifid);
+ ifhp->ifid_ino = node->hash_id;
+
+ return (0);
+}
+
+/*
+ * Read the block and then set the data pointer to correspond with the
+ * offset passed in. Only read in at most 'size' bytes, and then set 'size'
+ * to the number of bytes pointed to. If 'size' is zero, try to read in a
+ * whole extent.
+ *
+ * Note that *bp may be assigned error or not.
+ *
+ */
+static int
+udf_readatoffset(struct udf_node *node, int *size, off_t offset,
+ struct buf **bp, uint8_t **data)
+{
+ struct udf_mnt *udfmp = node->udfmp;
+ struct vnode *vp = node->i_vnode;
+ struct file_entry *fentry;
+ struct buf *bp1;
+ uint32_t max_size;
+ daddr_t sector;
+ off_t off;
+ int adj_size;
+ int error;
+
+ /*
+ * This call is made *not* only to detect UDF_INVALID_BMAP case,
+ * max_size is used as an ad-hoc read-ahead hint for "normal" case.
+ */
+ error = udf_bmap_internal(node, offset, &sector, &max_size);
+ if (error == UDF_INVALID_BMAP) {
+ /*
+ * This error means that the file *data* is stored in the
+ * allocation descriptor field of the file entry.
+ */
+ fentry = node->fentry;
+ *data = &fentry->data[le32toh(fentry->l_ea)];
+ *size = le32toh(fentry->l_ad);
+ if (offset >= *size)
+ *size = 0;
+ else {
+ *data += offset;
+ *size -= offset;
+ }
+ return (0);
+ } else if (error != 0) {
+ return (error);
+ }
+
+ /* Adjust the size so that it is within range */
+ if (*size == 0 || *size > max_size)
+ *size = max_size;
+
+ /*
+ * Because we will read starting at block boundary, we need to adjust
+ * how much we need to read so that all promised data is in.
+ * Also, we can't promise to read more than MAXBSIZE bytes starting
+ * from block boundary, so adjust what we promise too.
+ */
+ off = blkoff(udfmp, offset);
+ *size = min(*size, MAXBSIZE - off);
+ adj_size = (*size + off + udfmp->bmask) & ~udfmp->bmask;
+ *bp = NULL;
+ if ((error = bread(vp, lblkno(udfmp, offset), adj_size, NOCRED, bp))) {
+ printf("warning: udf_readlblks returned error %d\n", error);
+ /* note: *bp may be non-NULL */
+ return (error);
+ }
+
+ bp1 = *bp;
+ *data = (uint8_t *)&bp1->b_data[offset & udfmp->bmask];
+ return (0);
+}
+
+/*
+ * Translate a file offset into a logical block and then into a physical
+ * block.
+ * max_size - maximum number of bytes that can be read starting from given
+ * offset, rather than beginning of calculated sector number
+ */
+static int
+udf_bmap_internal(struct udf_node *node, off_t offset, daddr_t *sector,
+ uint32_t *max_size)
+{
+ struct udf_mnt *udfmp;
+ struct file_entry *fentry;
+ void *icb;
+ struct icb_tag *tag;
+ uint32_t icblen = 0;
+ daddr_t lsector;
+ int ad_offset, ad_num = 0;
+ int i, p_offset;
+
+ udfmp = node->udfmp;
+ fentry = node->fentry;
+ tag = &fentry->icbtag;
+
+ switch (le16toh(tag->strat_type)) {
+ case 4:
+ break;
+
+ case 4096:
+ printf("Cannot deal with strategy4096 yet!\n");
+ return (ENODEV);
+
+ default:
+ printf("Unknown strategy type %d\n", tag->strat_type);
+ return (ENODEV);
+ }
+
+ switch (le16toh(tag->flags) & 0x7) {
+ case 0:
+ /*
+ * The allocation descriptor field is filled with short_ad's.
+ * If the offset is beyond the current extent, look for the
+ * next extent.
+ */
+ do {
+ offset -= icblen;
+ ad_offset = sizeof(struct short_ad) * ad_num;
+ if (ad_offset > le32toh(fentry->l_ad)) {
+ printf("File offset out of bounds\n");
+ return (EINVAL);
+ }
+ icb = GETICB(short_ad, fentry,
+ le32toh(fentry->l_ea) + ad_offset);
+ icblen = GETICBLEN(short_ad, icb);
+ ad_num++;
+ } while(offset >= icblen);
+
+ lsector = (offset >> udfmp->bshift) +
+ le32toh(((struct short_ad *)(icb))->pos);
+
+ *max_size = icblen - offset;
+
+ break;
+ case 1:
+ /*
+ * The allocation descriptor field is filled with long_ad's
+ * If the offset is beyond the current extent, look for the
+ * next extent.
+ */
+ do {
+ offset -= icblen;
+ ad_offset = sizeof(struct long_ad) * ad_num;
+ if (ad_offset > le32toh(fentry->l_ad)) {
+ printf("File offset out of bounds\n");
+ return (EINVAL);
+ }
+ icb = GETICB(long_ad, fentry,
+ le32toh(fentry->l_ea) + ad_offset);
+ icblen = GETICBLEN(long_ad, icb);
+ ad_num++;
+ } while(offset >= icblen);
+
+ lsector = (offset >> udfmp->bshift) +
+ le32toh(((struct long_ad *)(icb))->loc.lb_num);
+
+ *max_size = icblen - offset;
+
+ break;
+ case 3:
+ /*
+ * This type means that the file *data* is stored in the
+ * allocation descriptor field of the file entry.
+ */
+ *max_size = 0;
+ *sector = node->hash_id + udfmp->part_start;
+
+ return (UDF_INVALID_BMAP);
+ case 2:
+ /* DirectCD does not use extended_ad's */
+ default:
+ printf("Unsupported allocation descriptor %d\n",
+ tag->flags & 0x7);
+ return (ENODEV);
+ }
+
+ *sector = lsector + udfmp->part_start;
+
+ /*
+ * Check the sparing table. Each entry represents the beginning of
+ * a packet.
+ */
+ if (udfmp->s_table != NULL) {
+ for (i = 0; i< udfmp->s_table_entries; i++) {
+ p_offset =
+ lsector - le32toh(udfmp->s_table->entries[i].org);
+ if ((p_offset < udfmp->p_sectors) && (p_offset >= 0)) {
+ *sector =
+ le32toh(udfmp->s_table->entries[i].map) +
+ p_offset;
+ break;
+ }
+ }
+ }
+
+ return (0);
+}