summaryrefslogtreecommitdiff
path: root/sys/isofs/udf
diff options
context:
space:
mode:
authorPedro Martelletto <pedro@cvs.openbsd.org>2005-03-29 17:24:53 +0000
committerPedro Martelletto <pedro@cvs.openbsd.org>2005-03-29 17:24:53 +0000
commit2ea47090ae4952e3ac013431c2d59c8cff9d1ba3 (patch)
tree5019e6a44e2282a0e9060861786f78bb4a0d3f16 /sys/isofs/udf
parent2a3144fa6131e8783c8c4e9d39fcfa9409520003 (diff)
Bring in UDF support from FreeBSD, disabled for now.
Diffstat (limited to 'sys/isofs/udf')
-rw-r--r--sys/isofs/udf/ecma167-udf.h375
-rw-r--r--sys/isofs/udf/osta.c514
-rw-r--r--sys/isofs/udf/osta.h30
-rw-r--r--sys/isofs/udf/udf.h136
-rw-r--r--sys/isofs/udf/udf_extern.h57
-rw-r--r--sys/isofs/udf/udf_vfsops.c766
-rw-r--r--sys/isofs/udf/udf_vnops.c1383
7 files changed, 3261 insertions, 0 deletions
diff --git a/sys/isofs/udf/ecma167-udf.h b/sys/isofs/udf/ecma167-udf.h
new file mode 100644
index 00000000000..5401dfb3a64
--- /dev/null
+++ b/sys/isofs/udf/ecma167-udf.h
@@ -0,0 +1,375 @@
+/* $OpenBSD: ecma167-udf.h,v 1.1 2005/03/29 17:24:52 pedro Exp $ */
+
+/*
+ * 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: src/sys/fs/udf/ecma167-udf.h,v 1.4 2002/09/23 18:54:30 alfred Exp $
+ */
+
+/* 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;
+
+#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 part_id[62];
+} __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 Sparing Tables */
+ uint8_t reserved1;
+ uint32_t st_size;
+ uint32_t st_loc[1];
+} __packed;
+
+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;
+};
+
+/* 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 implementaion 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
+
+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) ((struct ad_type *)(icb))->len
diff --git a/sys/isofs/udf/osta.c b/sys/isofs/udf/osta.c
new file mode 100644
index 00000000000..3fc07b6e0ba
--- /dev/null
+++ b/sys/isofs/udf/osta.c
@@ -0,0 +1,514 @@
+/* $OpenBSD: osta.c,v 1.1 2005/03/29 17:24:52 pedro Exp $ */
+
+/*
+ * Various routines from the OSTA 2.01 specs. Copyrights are included with
+ * each code segment. Slight whitespace modifications have been made for
+ * formatting purposes. Typos/bugs have been fixed.
+ *
+ * $FreeBSD: src/sys/fs/udf/osta.c,v 1.4 2005/01/06 18:10:41 imp Exp $
+ */
+
+#include <isofs/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 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 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/isofs/udf/osta.h b/sys/isofs/udf/osta.h
new file mode 100644
index 00000000000..fdc9c46db30
--- /dev/null
+++ b/sys/isofs/udf/osta.h
@@ -0,0 +1,30 @@
+/* $OpenBSD: osta.h,v 1.1 2005/03/29 17:24:52 pedro Exp $ */
+
+/*
+ * Prototypes for the OSTA functions
+ *
+ * $FreeBSD: src/sys/fs/udf/osta.h,v 1.2 2003/11/05 06:55:23 scottl Exp $
+ */
+
+#ifndef UNIX
+#define UNIX
+#endif
+
+#ifndef MAXLEN
+#define MAXLEN 255
+#endif
+
+/***********************************************************************
+ * The following two typedef's are to remove compiler dependancies.
+ * byte needs to be unsigned 8-bit, and unicode_t needs to be
+ * unsigned 16-bit.
+ */
+typedef 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/isofs/udf/udf.h b/sys/isofs/udf/udf.h
new file mode 100644
index 00000000000..b9aba264183
--- /dev/null
+++ b/sys/isofs/udf/udf.h
@@ -0,0 +1,136 @@
+/* $OpenBSD: udf.h,v 1.1 2005/03/29 17:24:52 pedro Exp $ */
+
+/*
+ * 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: src/sys/fs/udf/udf.h,v 1.9 2004/10/29 10:40:58 phk Exp $
+ */
+
+/*
+ * Ported to OpenBSD by Pedro Martelletto <pedro@openbsd.org> in February 2005.
+ */
+
+#define UDF_HASHTBLSIZE 100
+
+struct udf_node {
+ LIST_ENTRY(udf_node) le;
+ struct vnode *i_vnode;
+ struct vnode *i_devvp;
+ struct udf_mnt *udfmp;
+ struct lock i_lock;
+ dev_t i_dev;
+ ino_t hash_id;
+ long diroff;
+ struct file_entry *fentry;
+};
+
+struct udf_mnt {
+ int im_flags;
+ struct mount *im_mountp;
+ struct vnode *im_devvp;
+ dev_t im_dev;
+ int bsize;
+ int bshift;
+ int bmask;
+ uint32_t part_start;
+ uint32_t part_len;
+ uint64_t root_id;
+ struct vnode *root_vp;
+ struct long_ad root_icb;
+ LIST_HEAD(udf_hash_lh, udf_node) *hashtbl;
+ u_long hashsz;
+ struct mutex hash_mtx;
+ int p_sectors;
+ int s_table_entries;
+ struct udf_sparing_table *s_table;
+};
+
+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;
+};
+
+#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)
+
+static __inline int
+udf_readlblks(struct udf_mnt *udfmp, int sector, int size, struct buf **bp)
+{
+ return (RDSECTOR(udfmp->im_devvp, sector,
+ (size + udfmp->bmask) & ~udfmp->bmask, bp));
+}
+
+static __inline int
+udf_readalblks(struct udf_mnt *udfmp, int lsector, int size, struct buf **bp)
+{
+ daddr_t rablock, lblk;
+ int rasize;
+
+ lblk = (lsector + udfmp->part_start) << (udfmp->bshift - DEV_BSHIFT);
+ rablock = (lblk + 1) << udfmp->bshift;
+ rasize = size;
+
+ return (breadn(udfmp->im_devvp, lblk,
+ (size + udfmp->bmask) & ~udfmp->bmask,
+ &rablock, &rasize, 1, NOCRED, 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 (letoh32(icb->loc.lb_num));
+}
+
+int udf_allocv(struct mount *, struct vnode **, struct proc *);
+int udf_hashlookup(struct udf_mnt *, ino_t, int, struct vnode **);
+int udf_hashins(struct udf_node *);
+int udf_hashrem(struct udf_node *);
+int udf_checktag(struct desc_tag *, uint16_t);
diff --git a/sys/isofs/udf/udf_extern.h b/sys/isofs/udf/udf_extern.h
new file mode 100644
index 00000000000..8c5b95aba38
--- /dev/null
+++ b/sys/isofs/udf/udf_extern.h
@@ -0,0 +1,57 @@
+/* $OpenBSD: udf_extern.h,v 1.1 2005/03/29 17:24:52 pedro Exp $ */
+
+/*
+ * Written by Pedro Martelletto <pedro@openbsd.org> in February 2005.
+ * Public domain.
+ */
+
+/*
+ * udf_vfsops.c
+ */
+int udf_init(struct vfsconf *);
+int udf_mount(struct mount *, const char *, void *, struct nameidata *,
+ struct proc *);
+int udf_unmount(struct mount *, int, struct proc *);
+int udf_start(struct mount *, int, struct proc *);
+int udf_root(struct mount *, struct vnode **);
+int udf_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *);
+int udf_statfs(struct mount *, struct statfs *, struct proc *);
+int udf_vget(struct mount *, ino_t, struct vnode **);
+int udf_sync(struct mount *, int, struct ucred *, struct proc *);
+int udf_sysctl(int *, u_int, void *, size_t *, void *, size_t, struct proc *);
+int udf_checkexp(struct mount *, struct mbuf *, int *, struct ucred **);
+int udf_fhtovp(struct mount *, struct fid *, struct vnode **);
+int udf_vptofh(struct vnode *, struct fid *);
+
+/*
+ * udf_vnops.c
+ */
+int udf_access(void *v);
+int udf_getattr(void *v);
+int udf_open(void *v);
+int udf_close(void *v);
+int udf_ioctl(void *v);
+int udf_read(void *v);
+int udf_readdir(void *v);
+int udf_readlink(void *v);
+int udf_strategy(void *v);
+int udf_bmap(void *v);
+int udf_lookup(void *v);
+int udf_inactive(void *v);
+int udf_reclaim(void *v);
+int udf_lock(void *v);
+int udf_unlock(void *v);
+int udf_islocked(void *v);
+int udf_print(void *v);
+
+/*
+ * Memory pools.
+ */
+extern struct pool udf_trans_pool;
+extern struct pool udf_node_pool;
+extern struct pool udf_ds_pool;
+
+/*
+ * Set of UDF vnode operations.
+ */
+extern int (**udf_vnodeop_p)(void *);
diff --git a/sys/isofs/udf/udf_vfsops.c b/sys/isofs/udf/udf_vfsops.c
new file mode 100644
index 00000000000..feadd8cb485
--- /dev/null
+++ b/sys/isofs/udf/udf_vfsops.c
@@ -0,0 +1,766 @@
+/* $OpenBSD: udf_vfsops.c,v 1.1 2005/03/29 17:24:52 pedro Exp $ */
+
+/*
+ * 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: src/sys/fs/udf/udf_vfsops.c,v 1.25 2005/01/25 15:52:03 phk Exp $
+ */
+
+/*
+ * Ported to OpenBSD by Pedro Martelletto <pedro@openbsd.org> in February 2005.
+ */
+
+/*
+ * 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/buf.h>
+#include <sys/conf.h>
+#include <sys/dirent.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/pool.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/queue.h>
+#include <sys/vnode.h>
+#include <sys/endian.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <isofs/udf/ecma167-udf.h>
+#include <isofs/udf/osta.h>
+#include <isofs/udf/udf.h>
+#include <isofs/udf/udf_extern.h>
+
+struct pool udf_trans_pool;
+struct pool udf_node_pool;
+struct pool udf_ds_pool;
+
+int udf_find_partmaps(struct udf_mnt *, struct logvol_desc *);
+
+const struct vfsops udf_vfsops = {
+ .vfs_fhtovp = udf_fhtovp,
+ .vfs_init = udf_init,
+ .vfs_mount = udf_mount,
+ .vfs_start = udf_start,
+ .vfs_root = udf_root,
+ .vfs_quotactl = udf_quotactl,
+ .vfs_statfs = udf_statfs,
+ .vfs_sync = udf_sync,
+ .vfs_unmount = udf_unmount,
+ .vfs_vget = udf_vget,
+ .vfs_vptofh = udf_vptofh,
+ .vfs_sysctl = udf_sysctl,
+ .vfs_checkexp = udf_checkexp,
+};
+
+int udf_mountfs(struct vnode *, struct mount *, struct proc *);
+
+int
+udf_init(struct vfsconf *foo)
+{
+ pool_init(&udf_trans_pool, MAXNAMLEN * sizeof(unicode_t), 0, 0, 0,
+ "udftrpl", &pool_allocator_nointr);
+ pool_init(&udf_node_pool, sizeof(struct udf_node), 0, 0, 0,
+ "udfndpl", &pool_allocator_nointr);
+ pool_init(&udf_ds_pool, sizeof(struct udf_dirstream), 0, 0, 0,
+ "udfdspl", &pool_allocator_nointr);
+
+ return (0);
+}
+
+int
+udf_start(struct mount *mp, int flags, struct proc *p)
+{
+ return (0);
+}
+
+int
+udf_mount(struct mount *mp, const char *path, void *data,
+ struct nameidata *ndp, struct proc *p)
+{
+ struct vnode *devvp; /* vnode of the mount device */
+ struct udf_args args;
+ size_t len;
+ int error;
+
+ if ((mp->mnt_flag & MNT_RDONLY) == 0) {
+ mp->mnt_flag |= MNT_RDONLY;
+ printf("udf_mount: enforcing read-only mode\n");
+ }
+
+ /*
+ * No root filesystem support. Probably not a big deal, since the
+ * bootloader doesn't understand UDF.
+ */
+ if (mp->mnt_flag & MNT_ROOTFS)
+ return (EOPNOTSUPP);
+
+ error = copyin(data, &args, sizeof(struct udf_args));
+ if (error)
+ return (error);
+
+ if (args.fspec == NULL)
+ return (EINVAL);
+
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
+ if ((error = namei(ndp)))
+ return (error);
+
+ devvp = ndp->ni_vp;
+ if (devvp->v_type != VBLK) {
+ vrele(devvp);
+ return (ENOTBLK);
+ }
+
+ if (major(devvp->v_rdev) >= nblkdev) {
+ vrele(devvp);
+ return (ENXIO);
+ }
+
+ /* Check the access rights on the mount device */
+ if (p->p_ucred->cr_uid) {
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
+ error = VOP_ACCESS(devvp, VREAD, p->p_ucred, p);
+ VOP_UNLOCK(devvp, 0, p);
+ if (error) {
+ vrele(devvp);
+ return (error);
+ }
+ }
+
+ if ((error = udf_mountfs(devvp, mp, p))) {
+ vrele(devvp);
+ return (error);
+ }
+
+ /*
+ * Keep a copy of the mount information.
+ */
+ copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &len);
+ bzero(mp->mnt_stat.f_mntonname + len, MNAMELEN - len);
+ copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &len);
+ bzero(mp->mnt_stat.f_mntfromname + len, MNAMELEN - len);
+
+ 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 (tag->id != id)
+ return (EINVAL);
+
+ for (i = 0; i < 15; i++)
+ cksum = cksum + itag[i];
+ cksum = cksum - itag[4];
+
+ if (cksum == tag->cksum)
+ return (0);
+
+ return (EINVAL);
+}
+
+int
+udf_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p) {
+ struct buf *bp = NULL;
+ 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 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;
+
+ /*
+ * Disallow multiple mounts of the same device.
+ * Disallow mounting of a device that is currently in use
+ * (except for root, which might share swap device for miniroot).
+ * Flush out any old buffers remaining from a previous use.
+ */
+ if ((error = vfs_mountedon(devvp)))
+ return (error);
+ if (vcount(devvp) > 1 && devvp != rootvp)
+ return (EBUSY);
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
+ error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0);
+ VOP_UNLOCK(devvp, 0, p);
+ if (error)
+ return (error);
+
+ error = VOP_OPEN(devvp, FREAD, FSCRED, p);
+ if (error)
+ return (error);
+
+ MALLOC(udfmp, struct udf_mnt *, sizeof(struct udf_mnt), M_UDFMOUNT,
+ M_WAITOK);
+ bzero(udfmp, sizeof(struct udf_mnt));
+
+ mp->mnt_data = (qaddr_t)udfmp;
+ mp->mnt_stat.f_fsid.val[0] = devvp->v_rdev;
+ mp->mnt_stat.f_fsid.val[1] = makefstype(MOUNT_UDF);
+ mp->mnt_flag |= MNT_LOCAL;
+ udfmp->im_mountp = mp;
+ udfmp->im_dev = devvp->v_rdev;
+ udfmp->im_devvp = devvp;
+
+ bsize = 2048; /* XXX Should probe the media for its size. */
+
+ /*
+ * 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(bsize), 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 = letoh32(avdp.main_vds_ex.loc);
+ mvds_end = mvds_start + (letoh32(avdp.main_vds_ex.len) - 1) / bsize;
+ for (sector = mvds_start; sector < mvds_end; sector++) {
+ if ((error = bread(devvp, sector * btodb(bsize), 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 = letoh32(lvd->lb_size);
+ udfmp->bmask = udfmp->bsize - 1;
+ udfmp->bshift = ffs(udfmp->bsize) - 1;
+ fsd_part = letoh16(lvd->_lvd_use.fsd_loc.loc.part_num);
+ fsd_offset = letoh32(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 = letoh16(pd->part_num);
+ udfmp->part_len = letoh32(pd->part_len);
+ udfmp->part_start = letoh32(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 = letoh32(udfmp->root_icb.loc.lb_num) + udfmp->part_start;
+ size = letoh32(udfmp->root_icb.len);
+ if ((error = udf_readlblks(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;
+
+ mtx_init(&udfmp->hash_mtx, IPL_NONE);
+ udfmp->hashtbl = hashinit(UDF_HASHTBLSIZE, M_UDFMOUNT, M_WAITOK,
+ &udfmp->hashsz);
+
+ devvp->v_specmountpoint = mp;
+
+ return (0);
+
+bail:
+ if (udfmp != NULL)
+ FREE(udfmp, M_UDFMOUNT);
+ if (bp != NULL)
+ brelse(bp);
+ VOP_CLOSE(devvp, FREAD, FSCRED, p);
+
+ return (error);
+};
+
+int
+udf_unmount(struct mount *mp, int mntflags, struct proc *p)
+{
+ struct udf_mnt *udfmp;
+ struct vnode *devvp;
+ int error, flags = 0;
+
+ udfmp = VFSTOUDFFS(mp);
+ devvp = udfmp->im_devvp;
+
+ if (mntflags & MNT_FORCE)
+ flags |= FORCECLOSE;
+
+ if ((error = vflush(mp, NULL, flags)))
+ return (error);
+
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
+ vinvalbuf(devvp, V_SAVE, NOCRED, p, 0, 0);
+ error = VOP_CLOSE(devvp, FREAD, NOCRED, p);
+ VOP_UNLOCK(devvp, 0, p);
+ if (error)
+ return (error);
+
+ devvp->v_specmountpoint = NULL;
+ vrele(devvp);
+
+ if (udfmp->s_table != NULL)
+ FREE(udfmp->s_table, M_UDFMOUNT);
+
+ if (udfmp->hashtbl != NULL)
+ FREE(udfmp->hashtbl, M_UDFMOUNT);
+
+ FREE(udfmp, M_UDFMOUNT);
+
+ mp->mnt_data = (qaddr_t)0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+
+ return (0);
+}
+
+int
+udf_root(struct mount *mp, struct vnode **vpp)
+{
+ struct udf_mnt *udfmp;
+ struct vnode *vp;
+ ino_t id;
+ int error;
+
+ udfmp = VFSTOUDFFS(mp);
+
+ id = udf_getid(&udfmp->root_icb);
+
+ error = udf_vget(mp, id, vpp);
+ if (error)
+ return (error);
+
+ vp = *vpp;
+ vp->v_flag |= VROOT;
+ udfmp->root_vp = vp;
+
+ return (0);
+}
+
+int
+udf_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
+ struct proc *p)
+{
+ return (EOPNOTSUPP);
+}
+
+int
+udf_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
+{
+ 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_sync(struct mount *mp, int waitfor, struct ucred *cred, struct proc *p)
+{
+ return (0);
+}
+
+int
+udf_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
+{
+ struct buf *bp;
+ struct vnode *devvp;
+ struct udf_mnt *udfmp;
+ struct proc *p;
+ struct vnode *vp;
+ struct udf_node *unode;
+ struct file_entry *fe;
+ int error, sector, size;
+
+ p = curproc;
+ *vpp = NULL;
+ udfmp = VFSTOUDFFS(mp);
+
+ /* See if we already have this in the cache */
+ if ((error = udf_hashlookup(udfmp, ino, LK_EXCLUSIVE, vpp)) != 0)
+ return (error);
+ if (*vpp != NULL)
+ return (0);
+
+ /*
+ * Allocate memory and check the tag id's before grabbing a new
+ * vnode, since it's hard to roll back if there is a problem.
+ */
+ unode = pool_get(&udf_node_pool, PR_WAITOK);
+ bzero(unode, sizeof(struct udf_node));
+
+ /*
+ * 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);
+ pool_put(&udf_node_pool, unode);
+ return (error);
+ }
+
+ fe = (struct file_entry *)bp->b_data;
+ if (udf_checktag(&fe->tag, TAGID_FENTRY)) {
+ printf("Invalid file entry!\n");
+ pool_put(&udf_node_pool, unode);
+ brelse(bp);
+ return (ENOMEM);
+ }
+ size = UDF_FENTRY_SIZE + letoh32(fe->l_ea) + letoh32(fe->l_ad);
+ MALLOC(unode->fentry, struct file_entry *, size, M_UDFFENTRY,
+ M_NOWAIT);
+ if (unode->fentry == NULL) {
+ printf("Cannot allocate file entry block\n");
+ pool_put(&udf_node_pool, unode);
+ brelse(bp);
+ return (ENOMEM);
+ }
+
+ bcopy(bp->b_data, unode->fentry, size);
+
+ brelse(bp);
+ bp = NULL;
+
+ if ((error = udf_allocv(mp, &vp, p))) {
+ printf("Error from udf_allocv\n");
+ pool_put(&udf_node_pool, unode);
+ return (error);
+ }
+
+ unode->i_vnode = vp;
+ unode->hash_id = ino;
+ unode->i_devvp = udfmp->im_devvp;
+ unode->i_dev = udfmp->im_dev;
+ unode->udfmp = udfmp;
+ vp->v_data = unode;
+ VREF(udfmp->im_devvp);
+
+ lockinit(&unode->i_lock, PINOD, "unode", 0, 0);
+
+ /*
+ * udf_hashins() will lock the vnode for us.
+ */
+ udf_hashins(unode);
+
+ 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;
+ break;
+ case 10:
+ vp->v_type = VSOCK;
+ break;
+ case 12:
+ vp->v_type = VLNK;
+ break;
+ }
+
+ *vpp = vp;
+
+ return (0);
+}
+
+struct ifid {
+ u_short ifid_len;
+ u_short ifid_pad;
+ int ifid_ino;
+ long ifid_start;
+};
+
+int
+udf_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
+{
+ struct ifid *ifhp;
+ struct vnode *nvp;
+ int error;
+
+ ifhp = (struct ifid *)fhp;
+
+ if ((error = VFS_VGET(mp, ifhp->ifid_ino, &nvp)) != 0) {
+ *vpp = NULLVP;
+ return (error);
+ }
+
+ *vpp = nvp;
+
+ return (0);
+}
+
+int
+udf_vptofh(struct vnode *vp, struct fid *fhp)
+{
+ struct udf_node *node;
+ struct ifid *ifhp;
+
+ node = VTON(vp);
+ ifhp = (struct ifid *)fhp;
+ ifhp->ifid_len = sizeof(struct ifid);
+ ifhp->ifid_ino = node->hash_id;
+
+ return (0);
+}
+
+int
+udf_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
+ size_t newlen, struct proc *p)
+{
+ return (EINVAL);
+}
+
+int
+udf_checkexp(struct mount *mp, struct mbuf *nam, int *exflagsp,
+ struct ucred **credanonp)
+{
+ /* For the time being. */
+ return (EACCES);
+}
+
+int
+udf_find_partmaps(struct udf_mnt *udfmp, struct logvol_desc *lvd)
+{
+ union udf_pmap *pmap;
+ struct part_map_spare *pms;
+ struct regid *pmap_id;
+ struct buf *bp;
+ unsigned char regid_id[UDF_REGID_ID_SIZE + 1];
+ int i, ptype, psize, error;
+
+ for (i = 0; i < letoh32(lvd->n_pm); i++) {
+ pmap = (union udf_pmap *)&lvd->maps[i * UDF_PMAP_SIZE];
+ ptype = pmap->data[0];
+ psize = pmap->data[1];
+ if (((ptype != 1) && (ptype != 2)) ||
+ ((psize != UDF_PMAP_SIZE) && (psize != 6))) {
+ printf("Invalid partition map found\n");
+ return (1);
+ }
+
+ if (ptype == 1) {
+ /* Type 1 map. We don't care */
+ continue;
+ }
+
+ /* Type 2 map. Gotta find out the details */
+ pmap_id = (struct regid *)&pmap->data[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 = &pmap->pms;
+ MALLOC(udfmp->s_table, struct udf_sparing_table *,
+ letoh32(pms->st_size), M_UDFMOUNT, M_NOWAIT);
+ if (udfmp->s_table == NULL)
+ return (ENOMEM);
+
+ bzero(udfmp->s_table, letoh32(pms->st_size));
+
+ /* Calculate the number of sectors per packet. */
+ /* XXX Logical or physical? */
+ udfmp->p_sectors = letoh16(pms->packet_len) / udfmp->bsize;
+
+ /*
+ * XXX If reading the first Sparing Table fails, should look
+ * for another table.
+ */
+ if ((error = udf_readlblks(udfmp, letoh32(pms->st_loc[0]),
+ letoh32(pms->st_size), &bp)) != 0) {
+ if (bp != NULL)
+ brelse(bp);
+ printf("Failed to read Sparing Table at sector %d\n",
+ letoh32(pms->st_loc[0]));
+ return (error);
+ }
+ bcopy(bp->b_data, udfmp->s_table, letoh32(pms->st_size));
+ brelse(bp);
+
+ if (udf_checktag(&udfmp->s_table->tag, 0)) {
+ printf("Invalid sparing table found\n");
+ return (EINVAL);
+ }
+
+ /*
+ * See how many valid entries there are here. The list is
+ * supposed to be sorted. 0xfffffff0 and higher are not valid
+ */
+ for (i = 0; i < letoh16(udfmp->s_table->rt_l); i++) {
+ udfmp->s_table_entries = i;
+ if (letoh32(udfmp->s_table->entries[i].org) >=
+ 0xfffffff0)
+ break;
+ }
+ }
+
+ return (0);
+}
diff --git a/sys/isofs/udf/udf_vnops.c b/sys/isofs/udf/udf_vnops.c
new file mode 100644
index 00000000000..92c1043ac71
--- /dev/null
+++ b/sys/isofs/udf/udf_vnops.c
@@ -0,0 +1,1383 @@
+/* $OpenBSD: udf_vnops.c,v 1.1 2005/03/29 17:24:52 pedro Exp $ */
+
+/*
+ * 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: src/sys/fs/udf/udf_vnops.c,v 1.50 2005/01/28 14:42:16 phk Exp $
+ */
+
+/*
+ * Ported to OpenBSD by Pedro Martelletto <pedro@openbsd.org> in February 2005.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/pool.h>
+#include <sys/lock.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 <miscfs/specfs/specdev.h>
+
+#include <isofs/udf/ecma167-udf.h>
+#include <isofs/udf/osta.h>
+#include <isofs/udf/udf.h>
+#include <isofs/udf/udf_extern.h>
+
+int udf_readatoffset(struct udf_node *, int *, off_t, struct buf **,
+ uint8_t **);
+int udf_bmap_internal(struct udf_node *, off_t, daddr_t *, uint32_t *);
+
+int (**udf_vnodeop_p)(void *);
+struct vnodeopv_entry_desc udf_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_access_desc, udf_access }, /* access */
+ { &vop_bmap_desc, udf_bmap }, /* bmap */
+ { &vop_lookup_desc, udf_lookup }, /* lookup */
+ { &vop_getattr_desc, udf_getattr }, /* getattr */
+ { &vop_open_desc, udf_open }, /* open */
+ { &vop_close_desc, udf_close }, /* close */
+ { &vop_ioctl_desc, udf_ioctl }, /* ioctl */
+ { &vop_read_desc, udf_read }, /* read */
+ { &vop_readdir_desc, udf_readdir }, /* readdir */
+ { &vop_readlink_desc, udf_readlink }, /* readlink */
+ { &vop_inactive_desc, udf_inactive }, /* inactive */
+ { &vop_reclaim_desc, udf_reclaim }, /* reclaim */
+ { &vop_strategy_desc, udf_strategy }, /* strategy */
+ { &vop_lock_desc, udf_lock }, /* lock */
+ { &vop_unlock_desc, udf_unlock }, /* unlock */
+ { &vop_islocked_desc, udf_islocked }, /* islocked */
+ { &vop_print_desc, udf_print }, /* print */
+ { NULL, NULL }
+};
+struct vnodeopv_desc udf_vnodeop_opv_desc =
+ { &udf_vnodeop_p, udf_vnodeop_entries };
+
+#define UDF_INVALID_BMAP -1
+
+/* Look up a udf_node based on the ino_t passed in and return it's vnode */
+int
+udf_hashlookup(struct udf_mnt *udfmp, ino_t id, int flags, struct vnode **vpp)
+{
+ struct udf_node *node;
+ struct udf_hash_lh *lh;
+ struct proc *p = curproc;
+ int error;
+
+ *vpp = NULL;
+
+loop:
+ mtx_enter(&udfmp->hash_mtx);
+ lh = &udfmp->hashtbl[id & udfmp->hashsz];
+ if (lh == NULL)
+ return (ENOENT);
+ LIST_FOREACH(node, lh, le) {
+ if (node->hash_id == id) {
+ mtx_leave(&udfmp->hash_mtx);
+ error = vget(node->i_vnode, flags | LK_INTERLOCK, p);
+ if (error == ENOENT)
+ goto loop;
+ if (error)
+ return (error);
+ *vpp = node->i_vnode;
+ return (0);
+ }
+ }
+
+ mtx_leave(&udfmp->hash_mtx);
+
+ return (0);
+}
+
+int
+udf_hashins(struct udf_node *node)
+{
+ struct udf_mnt *udfmp;
+ struct udf_hash_lh *lh;
+ struct proc *p = curproc;
+
+ udfmp = node->udfmp;
+
+ vn_lock(node->i_vnode, LK_EXCLUSIVE | LK_RETRY, p);
+ mtx_enter(&udfmp->hash_mtx);
+ lh = &udfmp->hashtbl[node->hash_id & udfmp->hashsz];
+ if (lh == NULL)
+ LIST_INIT(lh);
+ LIST_INSERT_HEAD(lh, node, le);
+ mtx_leave(&udfmp->hash_mtx);
+
+ return (0);
+}
+
+int
+udf_hashrem(struct udf_node *node)
+{
+ struct udf_mnt *udfmp;
+ struct udf_hash_lh *lh;
+
+ udfmp = node->udfmp;
+
+ mtx_enter(&udfmp->hash_mtx);
+ lh = &udfmp->hashtbl[node->hash_id & udfmp->hashsz];
+ if (lh == NULL)
+ panic("hash entry is NULL, node->hash_id= %d\n", node->hash_id);
+ LIST_REMOVE(node, le);
+ mtx_leave(&udfmp->hash_mtx);
+
+ return (0);
+}
+
+int
+udf_allocv(struct mount *mp, struct vnode **vpp, struct proc *p)
+{
+ int error;
+ struct vnode *vp;
+
+ error = getnewvnode(VT_UDF, mp, udf_vnodeop_p, &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 = letoh32(node->fentry->perm);
+ flags = letoh16(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);
+}
+
+int
+udf_access(void *v)
+{
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap = v;
+ struct vnode *vp;
+ struct udf_node *node;
+ mode_t a_mode, mode;
+
+ vp = ap->a_vp;
+ node = VTON(vp);
+ a_mode = ap->a_mode;
+
+ if (a_mode & VWRITE) {
+ switch (vp->v_type) {
+ case VDIR:
+ case VLNK:
+ case VREG:
+ return (EROFS);
+ /* NOTREACHED */
+ default:
+ break;
+ }
+ }
+
+ mode = udf_permtomode(node);
+
+ return (vaccess(mode, node->fentry->uid, node->fentry->gid, a_mode,
+ ap->a_cred));
+}
+
+static int mon_lens[2][12] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+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);
+}
+
+/*
+ * XXX This is just a rough hack. Daylight savings isn't calculated and tv_nsec
+ * is ignored.
+ * Timezone calculation compliments of Julian Elischer <julian@elischer.org>.
+ */
+static void
+udf_timetotimespec(struct timestamp *time, struct timespec *t)
+{
+ int i, lpyear, daysinyear, year;
+ union {
+ uint16_t u_tz_offset;
+ int16_t s_tz_offset;
+ } tz;
+
+ t->tv_nsec = 0;
+
+ /* DirectCD seems to like using bogus year values */
+ year = letoh16(time->year);
+ if (year < 1970) {
+ t->tv_sec = 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 * 3600 * 24;
+
+ /* Calclulate the month */
+ lpyear = udf_isaleapyear(year);
+ for (i = 1; i < time->month; i++)
+ t->tv_sec += mon_lens[lpyear][i] * 3600 * 24;
+
+ /* Speed up the calculation */
+ if (year > 1979)
+ t->tv_sec += 315532800;
+ if (year > 1989)
+ t->tv_sec += 315619200;
+ if (year > 1999)
+ t->tv_sec += 315532800;
+ for (i = 2000; i < year; i++) {
+ daysinyear = udf_isaleapyear(i) + 365 ;
+ t->tv_sec += daysinyear * 3600 * 24;
+ }
+
+ /*
+ * 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.u_tz_offset = letoh16(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 ((time->type_tz & 0x1000) && (tz.s_tz_offset != -2047))
+ t->tv_sec -= tz.s_tz_offset * 60;
+
+ return;
+}
+
+int
+udf_getattr(void *v)
+{
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *v_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap = v;
+ struct vnode *vp;
+ struct udf_node *node;
+ struct vattr *vap;
+ struct file_entry *fentry;
+ struct timespec ts;
+
+ ts.tv_sec = 0;
+
+ vp = ap->a_vp;
+ vap = ap->a_vap;
+ node = VTON(vp);
+ fentry = node->fentry;
+
+ vap->va_fsid = node->i_dev;
+ vap->va_fileid = node->hash_id;
+ vap->va_mode = udf_permtomode(node);
+ vap->va_nlink = letoh16(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 = (letoh32(fentry->uid) == -1) ? 0 : letoh32(fentry->uid);
+ vap->va_gid = (letoh32(fentry->gid) == -1) ? 0 : letoh32(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 = 0; /* XXX */
+ 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 =
+ letoh64(fentry->logblks_rec) * node->udfmp->bsize;
+ } else {
+ vap->va_size = node->udfmp->bsize;
+ }
+ } else {
+ vap->va_size = letoh64(fentry->inf_len);
+ }
+ vap->va_flags = 0;
+ vap->va_gen = 1;
+ vap->va_blocksize = node->udfmp->bsize;
+ vap->va_bytes = letoh64(fentry->inf_len);
+ vap->va_type = vp->v_type;
+ vap->va_filerev = 0; /* XXX */
+
+ return (0);
+}
+
+int
+udf_open(void *v)
+{
+ /*
+ * Dummy. Nothing to be done at this point.
+ */
+
+ return (0);
+}
+
+int
+udf_close(void *v)
+{
+ /*
+ * Dummy. Nothing to be done at this point.
+ */
+
+ return (0);
+}
+
+/*
+ * File specific ioctls.
+ */
+int
+udf_ioctl(void *v)
+{
+ return (ENOTTY);
+}
+
+/*
+ * I'm not sure that this has much value in a read-only filesystem, but
+ * cd9660 has it too.
+ */
+#if 0
+static int
+udf_pathconf(struct vop_pathconf_args *a)
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = 65535;
+ 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_NO_TRUNC:
+ *ap->a_retval = 1;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+}
+#endif
+
+int
+udf_read(void *v)
+{
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap = v;
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct udf_node *node = VTON(vp);
+ struct buf *bp;
+ uint8_t *data;
+ off_t fsize, offset;
+ int error = 0;
+ int size;
+
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+
+ fsize = letoh64(node->fentry->inf_len);
+
+ while (uio->uio_offset < fsize && uio->uio_resid > 0) {
+ offset = uio->uio_offset;
+ if (uio->uio_resid + offset <= fsize)
+ size = uio->uio_resid;
+ else
+ size = fsize - offset;
+ error = udf_readatoffset(node, &size, offset, &bp, &data);
+ if (error == 0)
+ error = uiomove(data, size, uio);
+ if (bp != NULL)
+ brelse(bp);
+ if (error)
+ break;
+ };
+
+ 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;
+ int i, unilen = 0, destlen;
+
+ /* allocate a buffer big enough to hold an 8->16 bit expansion */
+ transname = pool_get(&udf_trans_pool, PR_WAITOK);
+
+ if ((unilen = udf_UncompressUnicode(len, cs0string, transname)) == -1) {
+ printf("udf: Unicode translation failed\n");
+ pool_put(&udf_trans_pool, 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;
+
+ pool_put(&udf_trans_pool, transname);
+ destname[unilen] = 0;
+ destlen = unilen;
+
+ return (destlen);
+}
+
+/*
+ * Compare a CS0 dstring with a name passed in from the VFS layer. Return
+ * 0 on a successful match, nonzero therwise. 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 = pool_get(&udf_trans_pool, PR_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);
+
+ pool_put(&udf_trans_pool, 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 = pool_get(&udf_ds_pool, PR_WAITOK);
+ bzero(ds, sizeof(struct udf_dirstream));
+
+ 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 + letoh16(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.
+ */
+ MALLOC(ds->buf, uint8_t*, ds->udfmp->bsize, M_UDFFID,
+ M_WAITOK);
+ bzero(ds->buf, ds->udfmp->bsize);
+ 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 + letoh16(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 = letoh16(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->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);
+
+ pool_put(&udf_ds_pool, ds);
+}
+
+int
+udf_readdir(void *v)
+{
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ int *a_eofflag;
+ u_long **a_cookies;
+ int *ncookies;
+ } */ *ap = v;
+ 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;
+
+#define GENERIC_DIRSIZ(dp) \
+ ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
+
+ vp = ap->a_vp;
+ uio = ap->a_uio;
+ node = VTON(vp);
+ udfmp = node->udfmp;
+ uiodir.eofflag = 1;
+
+ if (ap->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;
+ MALLOC(cookies, u_long *, 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, letoh64(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");
+ 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_name[1] = '\0';
+ dir.d_namlen = 1;
+ dir.d_reclen = GENERIC_DIRSIZ(&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_name[2] = '\0';
+ dir.d_namlen = 2;
+ dir.d_reclen = GENERIC_DIRSIZ(&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);
+ uiodir.dirent = &dir;
+ error = udf_uiodir(&uiodir, dir.d_reclen, uio,
+ ds->this_off);
+ }
+ if (error) {
+ printf("uiomove returned %d\n", error);
+ break;
+ }
+
+ }
+
+#undef GENERIC_DIRSIZ
+
+ /* tell the calling layer whether we need to be called again */
+ *ap->a_eofflag = uiodir.eofflag;
+ uio->uio_offset = ds->offset + ds->off;
+
+ if (!error)
+ error = ds->error;
+
+ udf_closedir(ds);
+
+ if (ap->a_ncookies != NULL) {
+ if (error)
+ FREE(cookies, M_TEMP);
+ else {
+ *ap->a_ncookies = uiodir.acookies;
+ *ap->a_cookies = cookies;
+ }
+ }
+
+ return (error);
+}
+
+/* Are there any implementations out there that do soft-links? */
+int
+udf_readlink(void *v)
+{
+ return (EOPNOTSUPP);
+}
+
+int
+udf_strategy(void *v)
+{
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap = v;
+ struct buf *bp;
+ struct vnode *vp;
+ struct udf_node *node;
+ int maxsize, s, error;
+
+ bp = ap->a_bp;
+ vp = bp->b_vp;
+ node = VTON(vp);
+
+ /* cd9660 has this test reversed, but it seems more logical this way */
+ if (bp->b_blkno != bp->b_lblkno) {
+ /*
+ * Files that are embedded in the fentry don't translate well
+ * to a block number. Reject.
+ */
+ if (udf_bmap_internal(node, bp->b_lblkno * node->udfmp->bsize,
+ &bp->b_lblkno, &maxsize)) {
+ clrbuf(bp);
+ bp->b_blkno = -1;
+ }
+ } else {
+ error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL);
+ if (error) {
+ bp->b_error = error;
+ bp->b_flags |= B_ERROR;
+ s = splbio();
+ biodone(bp);
+ splx(s);
+ return (error);
+ }
+
+ if ((long)bp->b_blkno == -1)
+ clrbuf(bp);
+ }
+
+ if ((long)bp->b_blkno == -1) {
+ s = splbio();
+ biodone(bp);
+ splx(s);
+ } else {
+ bp->b_dev = vp->v_rdev;
+ VOCALL(node->i_devvp->v_op, VOFFSET(vop_strategy), ap);
+ }
+
+ return (0);
+}
+
+int
+udf_lock(void *v)
+{
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ int a_flags;
+ struct proc *a_p;
+ } */ *ap = v;
+
+ struct vnode *vp = ap->a_vp;
+
+ return (lockmgr(&VTON(vp)->i_lock, ap->a_flags, &vp->v_interlock,
+ ap->a_p));
+}
+
+int
+udf_unlock(void *v)
+{
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ int a_flags;
+ struct proc *a_p;
+ } */ *ap= v;
+
+ struct vnode *vp = ap->a_vp;
+
+ return (lockmgr(&VTON(vp)->i_lock, ap->a_flags | LK_RELEASE,
+ &vp->v_interlock, ap->a_p));
+}
+
+int
+udf_islocked(void *v)
+{
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap = v;
+
+ return (lockstatus(&VTON(ap->a_vp)->i_lock));
+}
+
+int
+udf_print(void *v)
+{
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap = v;
+ struct vnode *vp = ap->a_vp;
+ struct udf_node *node = VTON(vp);
+
+ /*
+ * Complete the information given by vprint().
+ */
+ printf("tag VT_UDF, hash id %u\n", node->hash_id);
+ lockmgr_printinfo(&node->i_lock);
+ printf("\n");
+
+ return (0);
+}
+
+int
+udf_bmap(void *v)
+{
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap = v;
+ struct udf_node *node;
+ uint32_t max_size;
+ daddr_t lsector;
+ int error;
+
+ node = VTON(ap->a_vp);
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = node->i_devvp;
+ if (ap->a_bnp == NULL)
+ return (0);
+
+ error = udf_bmap_internal(node, ap->a_bn * node->udfmp->bsize, &lsector,
+ &max_size);
+ if (error)
+ return (error);
+
+ /* Translate logical to physical sector number */
+ *ap->a_bnp = lsector << (node->udfmp->bshift - DEV_BSHIFT);
+
+ /* Punt on read-ahead for now */
+ if (ap->a_runp)
+ *ap->a_runp = 0;
+
+ return (0);
+}
+
+/*
+ * The all powerful VOP_LOOKUP().
+ */
+int
+udf_lookup(void *v)
+{
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap = v;
+ struct vnode *dvp;
+ struct vnode *tdp = NULL;
+ struct vnode **vpp = ap->a_vpp;
+ struct udf_node *node;
+ struct udf_mnt *udfmp;
+ struct fileid_desc *fid = NULL;
+ struct udf_dirstream *ds;
+ struct proc *p;
+ u_long nameiop;
+ u_long flags;
+ char *nameptr;
+ long namelen;
+ ino_t id = 0;
+ int offset, error = 0;
+ int numdirpasses, fsize;
+
+ extern struct nchstats nchstats;
+
+ dvp = ap->a_dvp;
+ node = VTON(dvp);
+ udfmp = node->udfmp;
+ nameiop = ap->a_cnp->cn_nameiop;
+ flags = ap->a_cnp->cn_flags;
+ nameptr = ap->a_cnp->cn_nameptr;
+ namelen = ap->a_cnp->cn_namelen;
+ fsize = letoh64(node->fentry->inf_len);
+ p = ap->a_cnp->cn_proc;
+
+ /*
+ * Make sure the process can scan the requested directory.
+ */
+ error = VOP_ACCESS(dvp, VEXEC, ap->a_cnp->cn_cred, p);
+ if (error)
+ return (error);
+
+ /*
+ * Check if the (directory, name) tuple has been already cached.
+ */
+ error = cache_lookup(dvp, vpp, ap->a_cnp);
+ if (error >= 0)
+ return (error);
+ else
+ error = 0;
+
+ /*
+ * If dvp is what's being looked up, then return it.
+ */
+ if (ap->a_cnp->cn_namelen == 1 && ap->a_cnp->cn_nameptr[0] == '.') {
+ VREF(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+
+ /*
+ * 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/isofs/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) {
+ /* Check for a valid FID tag. */
+ 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;
+
+ if (error) {
+ udf_closedir(ds);
+ return (error);
+ }
+
+ /* Did we have a match? */
+ if (id) {
+ error = udf_vget(udfmp->im_mountp, id, &tdp);
+ if (!error) {
+ /*
+ * 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++;
+ if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) {
+ ap->a_cnp->cn_flags |= PDIRUNLOCK;
+ VOP_UNLOCK(dvp, 0, p);
+ }
+
+ *vpp = tdp;
+ }
+ } else {
+ /* Name wasn't found on this pass. Do another pass? */
+ if (numdirpasses == 2) {
+ numdirpasses--;
+ offset = 0;
+ udf_closedir(ds);
+ goto lookloop;
+ }
+
+ if ((flags & ISLASTCN) &&
+ (nameiop == CREATE || nameiop == RENAME)) {
+ error = EROFS;
+ } else {
+ error = ENOENT;
+ }
+ }
+
+ /*
+ * Cache the result of this lookup.
+ */
+ if (flags & MAKEENTRY)
+ cache_enter(dvp, *vpp, ap->a_cnp);
+
+ udf_closedir(ds);
+
+ return (error);
+}
+
+int
+udf_inactive(void *v)
+{
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ struct proc *a_p;
+ } */ *ap = v;
+ struct vnode *vp = ap->a_vp;
+ struct proc *p = ap->a_p;
+
+ /*
+ * No need to sync anything, so just unlock the vnode and return.
+ */
+ VOP_UNLOCK(vp, 0, p);
+
+ return (0);
+}
+
+int
+udf_reclaim(void *v)
+{
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ struct proc *a_p;
+ } */ *ap = v;
+ struct vnode *vp;
+ struct udf_node *unode;
+
+ vp = ap->a_vp;
+ unode = VTON(vp);
+
+ if (unode != NULL) {
+ udf_hashrem(unode);
+ if (unode->i_devvp) {
+ vrele(unode->i_devvp);
+ unode->i_devvp = 0;
+ }
+
+ if (unode->fentry != NULL)
+ FREE(unode->fentry, M_UDFFENTRY);
+ pool_put(&udf_node_pool, unode);
+ vp->v_data = NULL;
+ }
+
+ 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.
+ *
+ */
+int
+udf_readatoffset(struct udf_node *node, int *size, off_t offset,
+ struct buf **bp, uint8_t **data)
+{
+ struct udf_mnt *udfmp;
+ struct file_entry *fentry = NULL;
+ struct buf *bp1;
+ uint32_t max_size;
+ daddr_t sector;
+ int error;
+
+ udfmp = node->udfmp;
+
+ *bp = NULL;
+ 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[letoh32(fentry->l_ea)];
+ *size = letoh32(fentry->l_ad);
+ 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;
+ *size = min(*size, MAXBSIZE);
+
+ if ((error = udf_readlblks(udfmp, sector, *size, 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->bsize];
+ return (0);
+}
+
+/*
+ * Translate a file offset into a logical block and then into a physical
+ * block.
+ */
+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 (letoh16(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 (letoh16(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 > letoh32(fentry->l_ad)) {
+ printf("File offset out of bounds\n");
+ return (EINVAL);
+ }
+ icb = GETICB(short_ad, fentry,
+ letoh32(fentry->l_ea) + ad_offset);
+ icblen = GETICBLEN(short_ad, icb);
+ ad_num++;
+ } while(offset >= icblen);
+
+ lsector = (offset >> udfmp->bshift) +
+ ((struct short_ad *)(icb))->pos;
+
+ *max_size = GETICBLEN(short_ad, icb);
+
+ 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 > letoh32(fentry->l_ad)) {
+ printf("File offset out of bounds\n");
+ return (EINVAL);
+ }
+ icb = GETICB(long_ad, fentry,
+ letoh32(fentry->l_ea) + ad_offset);
+ icblen = GETICBLEN(long_ad, icb);
+ ad_num++;
+ } while(offset >= icblen);
+
+ lsector = (offset >> udfmp->bshift) +
+ letoh32(((struct long_ad *)(icb))->loc.lb_num);
+
+ *max_size = GETICBLEN(long_ad, icb);
+
+ 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 - letoh32(udfmp->s_table->entries[i].org);
+ if ((p_offset < udfmp->p_sectors) && (p_offset >= 0)) {
+ *sector =
+ letoh32(udfmp->s_table->entries[i].map) +
+ p_offset;
+ break;
+ }
+ }
+ }
+
+ return (0);
+}