summaryrefslogtreecommitdiffstats
path: root/sys/fs
diff options
context:
space:
mode:
authorscottl <scottl@FreeBSD.org>2002-04-14 16:52:14 +0000
committerscottl <scottl@FreeBSD.org>2002-04-14 16:52:14 +0000
commit1fd1b83baf376aa6cdbc4fee6c646c2e732250cd (patch)
treecb837dcaf5010099c0b2e9ce021a70b5e6f736ef /sys/fs
parent0559ebe0fd6ed993e11e306ef21ed5a9cb6da7f9 (diff)
downloadFreeBSD-src-1fd1b83baf376aa6cdbc4fee6c646c2e732250cd.zip
FreeBSD-src-1fd1b83baf376aa6cdbc4fee6c646c2e732250cd.tar.gz
Actually add the UDF files!
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/udf/ecma167-udf.h368
-rw-r--r--sys/fs/udf/osta.c466
-rw-r--r--sys/fs/udf/osta.h27
-rw-r--r--sys/fs/udf/udf.h115
-rw-r--r--sys/fs/udf/udf_mount.h34
-rw-r--r--sys/fs/udf/udf_vfsops.c744
-rw-r--r--sys/fs/udf/udf_vnops.c1232
7 files changed, 2986 insertions, 0 deletions
diff --git a/sys/fs/udf/ecma167-udf.h b/sys/fs/udf/ecma167-udf.h
new file mode 100644
index 0000000..a3a1c5a
--- /dev/null
+++ b/sys/fs/udf/ecma167-udf.h
@@ -0,0 +1,368 @@
+/*-
+ * 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 {
+ u_int16_t id;
+ u_int16_t descriptor_ver;
+ u_int8_t cksum;
+ u_int8_t reserved;
+ u_int16_t serial_num;
+ u_int16_t desc_crc;
+ u_int16_t desc_crc_len;
+ u_int32_t tag_loc;
+} __attribute__ ((packed));
+
+/* Recorded Address [4/7.1] */
+struct lb_addr {
+ u_int32_t lb_num;
+ u_int16_t part_num;
+} __attribute__ ((packed));
+
+/* Extent Descriptor [3/7.1] */
+struct extent_ad {
+ u_int32_t len;
+ u_int32_t loc;
+} __attribute__ ((packed));
+
+/* Short Allocation Descriptor [4/14.14.1] */
+struct short_ad {
+ u_int32_t len;
+ u_int32_t pos;
+} __attribute__ ((packed));
+
+/* Long Allocation Descriptor [4/14.14.2] */
+struct long_ad {
+ u_int32_t len;
+ struct lb_addr loc;
+ u_int16_t ad_flags;
+ u_int32_t ad_id;
+} __attribute__ ((packed));
+
+/* Extended Allocation Descriptor [4/14.14.3] */
+struct ext_ad {
+ u_int32_t ex_len;
+ u_int32_t rec_len;
+ u_int32_t inf_len;
+ struct lb_addr ex_loc;
+ u_int8_t reserved[2];
+} __attribute__ ((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 {
+ u_int8_t type;
+ u_int8_t inf[63];
+} __attribute__ ((packed));
+
+/* Timestamp [1/7.3] */
+struct timestamp {
+ u_int16_t type_tz;
+ u_int16_t year;
+ u_int8_t month;
+ u_int8_t day;
+ u_int8_t hour;
+ u_int8_t minute;
+ u_int8_t second;
+ u_int8_t centisec;
+ u_int8_t hund_usec;
+ u_int8_t usec;
+} __attribute__ ((packed));
+
+/* Entity Identifier [1/7.4] */
+#define UDF_REGID_ID_SIZE 23
+struct regid {
+ u_int8_t flags;
+ u_int8_t id[UDF_REGID_ID_SIZE];
+ u_int8_t id_suffix[8];
+} __attribute__ ((packed));
+
+/* ICB Tag [4/14.6] */
+struct icb_tag {
+ u_int32_t prev_num_dirs;
+ u_int16_t strat_type;
+ u_int8_t strat_param[2];
+ u_int16_t max_num_entries;
+ u_int8_t reserved;
+ u_int8_t file_type;
+ struct lb_addr parent_icb;
+ u_int16_t flags;
+} __attribute__ ((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;
+} __attribute__ ((packed));
+
+/* Volume Descriptor Pointer [3/10.3] */
+struct vol_desc_ptr {
+ struct desc_tag tag;
+ u_int32_t vds_number;
+ struct extent_ad next_vds_ex;
+} __attribute__ ((packed));
+
+/* Primary Volume Descriptor [3/10.1] */
+struct pri_vol_desc {
+ struct desc_tag tag;
+ u_int32_t seq_num;
+ u_int32_t pdv_num;
+ char vol_id[32];
+ u_int16_t vds_num;
+ u_int16_t max_vol_seq;
+ u_int16_t ichg_lvl;
+ u_int16_t max_ichg_lvl;
+ u_int32_t charset_list;
+ u_int32_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;
+ u_int8_t imp_use[64];
+ u_int32_t prev_vds_lov;
+ u_int16_t flags;
+ u_int8_t reserved[22];
+} __attribute__ ((packed));
+
+/* Logical Volume Descriptor [3/10.6] */
+struct logvol_desc {
+ struct desc_tag tag;
+ u_int32_t seq_num;
+ struct charspec desc_charset;
+ char logvol_id[128];
+ u_int32_t lb_size;
+ struct regid domain_id;
+ union {
+ struct long_ad fsd_loc;
+ u_int8_t logvol_content_use[16];
+ } _lvd_use;
+ u_int32_t mt_l; /* Partition map length */
+ u_int32_t n_pm; /* Number of partition maps */
+ struct regid imp_id;
+ u_int8_t imp_use[128];
+ struct extent_ad integrity_seq_id;
+ u_int8_t maps[1];
+} __attribute__ ((packed));
+
+#define UDF_PMAP_SIZE 64
+
+/* Type 1 Partition Map [3/10.7.2] */
+struct part_map_1 {
+ u_int8_t type;
+ u_int8_t len;
+ u_int16_t vol_seq_num;
+ u_int16_t part_num;
+} __attribute__ ((packed));
+
+/* Type 2 Partition Map [3/10.7.3] */
+struct part_map_2 {
+ u_int8_t type;
+ u_int8_t len;
+ u_int8_t part_id[62];
+} __attribute__ ((packed));
+
+/* Virtual Partition Map [UDF 2.01/2.2.8] */
+struct part_map_virt {
+ u_int8_t type;
+ u_int8_t len;
+ u_int8_t reserved[2];
+ struct regid id;
+ u_int16_t vol_seq_num;
+ u_int16_t part_num;
+ u_int8_t reserved1[24];
+} __attribute__ ((packed));
+
+/* Sparable Partition Map [UDF 2.01/2.2.9] */
+struct part_map_spare {
+ u_int8_t type;
+ u_int8_t len;
+ u_int8_t reserved[2];
+ struct regid id;
+ u_int16_t vol_seq_num;
+ u_int16_t part_num;
+ u_int16_t packet_len;
+ u_int8_t n_st; /* Number of Sparing Tables */
+ u_int8_t reserved1;
+ u_int32_t st_size;
+ u_int32_t st_loc[1];
+} __attribute__ ((packed));
+
+union udf_pmap {
+ u_int8_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 {
+ u_int32_t org;
+ u_int32_t map;
+} __attribute__ ((packed));
+
+/* Sparing Table [UDF 2.01/2.2.11] */
+struct udf_sparing_table {
+ struct desc_tag tag;
+ struct regid id;
+ u_int16_t rt_l; /* Relocation Table len */
+ u_int8_t reserved[2];
+ u_int32_t seq_num;
+ struct spare_map_entry entries[1];
+} __attribute__ ((packed));
+
+/* Partition Descriptor [3/10.5] */
+struct part_desc {
+ struct desc_tag tag;
+ u_int32_t seq_num;
+ u_int16_t flags;
+ u_int16_t part_num;
+ struct regid contents;
+ u_int8_t contents_use[128];
+ u_int32_t access_type;
+ u_int32_t start_loc;
+ u_int32_t part_len;
+ struct regid imp_id;
+ u_int8_t imp_use[128];
+ u_int8_t reserved[156];
+} __attribute__ ((packed));
+
+/* File Set Descriptor [4/14.1] */
+struct fileset_desc {
+ struct desc_tag tag;
+ struct timestamp time;
+ u_int16_t ichg_lvl;
+ u_int16_t max_ichg_lvl;
+ u_int32_t charset_list;
+ u_int32_t max_charset_list;
+ u_int32_t fileset_num;
+ u_int32_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;
+ u_int8_t reserved[32];
+} __attribute__ ((packed));
+
+/* File Identifier Descriptor [4/14.4] */
+struct fileid_desc {
+ struct desc_tag tag;
+ u_int16_t file_num;
+ u_int8_t file_char;
+ u_int8_t l_fi; /* Length of file identifier area */
+ struct long_ad icb;
+ u_int16_t l_iu; /* Length of implementaion use area */
+ u_int8_t data[1];
+} __attribute__ ((packed));
+#define UDF_FID_SIZE 38
+
+/* File Entry [4/14.9] */
+struct file_entry {
+ struct desc_tag tag;
+ struct icb_tag icbtag;
+ u_int32_t uid;
+ u_int32_t gid;
+ u_int32_t perm;
+ u_int16_t link_cnt;
+ u_int8_t rec_format;
+ u_int8_t rec_disp_attr;
+ u_int32_t rec_len;
+ u_int64_t inf_len;
+ u_int64_t logblks_rec;
+ struct timestamp atime;
+ struct timestamp mtime;
+ struct timestamp attrtime;
+ u_int32_t ckpoint;
+ struct long_ad ex_attr_icb;
+ struct regid imp_id;
+ u_int64_t unique_id;
+ u_int32_t l_ea; /* Length of extended attribute area */
+ u_int32_t l_ad; /* Length of allocation descriptors */
+ u_int8_t data[1];
+} __attribute ((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/fs/udf/osta.c b/sys/fs/udf/osta.c
new file mode 100644
index 0000000..c97dff1
--- /dev/null
+++ b/sys/fs/udf/osta.c
@@ -0,0 +1,466 @@
+/*
+ * 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.
+ */
+
+#include <stddef.h>
+/***********************************************************************
+ * Takes an OSTA CS0 compressed unicode name, and converts
+ * it to Unicode.
+ * The Unicode output will be in the byte order
+ * that the local compiler uses for 16-bit values.
+ * NOTE: This routine only performs error checking on the compID.
+ * It is up to the user to ensure that the unicode buffer is large
+ * enough, and that the compressed unicode name is correct.
+ *
+ * RETURN VALUE
+ *
+ * The number of unicode characters which were uncompressed.
+ * A -1 is returned if the compression ID is invalid.
+ */
+int
+udf_UncompressUnicode(
+ int numberOfBytes, /* (Input) number of bytes read from media. */
+ byte *UDFCompressed, /* (Input) bytes read from media. */
+ unicode_t *unicode) /* (Output) uncompressed unicode characters. */
+{
+ unsigned int compID;
+ int returnValue, unicodeIndex, byteIndex;
+
+ /* Use UDFCompressed to store current byte being read. */
+ compID = UDFCompressed[0];
+
+ /* First check for valid compID. */
+ if (compID != 8 && compID != 16) {
+ returnValue = -1;
+ } else {
+ unicodeIndex = 0;
+ byteIndex = 1;
+
+ /* Loop through all the bytes. */
+ while (byteIndex < numberOfBytes) {
+ if (compID == 16) {
+ /* Move the first byte to the high bits of the
+ * unicode char.
+ */
+ unicode[unicodeIndex] =
+ UDFCompressed[byteIndex++] << 8;
+ } else {
+ unicode[unicodeIndex] = 0;
+ }
+ if (byteIndex < numberOfBytes) {
+ /*Then the next byte to the low bits. */
+ unicode[unicodeIndex] |=
+ UDFCompressed[byteIndex++];
+ }
+ unicodeIndex++;
+ }
+ returnValue = unicodeIndex;
+ }
+ return(returnValue);
+}
+
+/***********************************************************************
+ * DESCRIPTION:
+ * Takes a string of unicode wide characters and returns an OSTA CS0
+ * compressed unicode string. The unicode MUST be in the byte order of
+ * the compiler in order to obtain correct results. Returns an error
+ * if the compression ID is invalid.
+ *
+ * NOTE: This routine assumes the implementation already knows, by
+ * the local environment, how many bits are appropriate and
+ * therefore does no checking to test if the input characters fit
+ * into that number of bits or not.
+ *
+ * RETURN VALUE
+ *
+ * The total number of bytes in the compressed OSTA CS0 string,
+ * including the compression ID.
+ * A -1 is returned if the compression ID is invalid.
+ */
+int
+udf_CompressUnicode(
+ int numberOfChars, /* (Input) number of unicode characters. */
+ int compID, /* (Input) compression ID to be used. */
+ unicode_t *unicode, /* (Input) unicode characters to compress. */
+ byte *UDFCompressed) /* (Output) compressed string, as bytes. */
+{
+ int byteIndex, unicodeIndex;
+
+ if (compID != 8 && compID != 16) {
+ byteIndex = -1; /* Unsupported compression ID ! */
+ } else {
+ /* Place compression code in first byte. */
+ UDFCompressed[0] = compID;
+
+ byteIndex = 1;
+ unicodeIndex = 0;
+ while (unicodeIndex < numberOfChars) {
+ if (compID == 16) {
+ /* First, place the high bits of the char
+ * into the byte stream.
+ */
+ UDFCompressed[byteIndex++] =
+ (unicode[unicodeIndex] & 0xFF00) >> 8;
+ }
+ /*Then place the low bits into the stream. */
+ UDFCompressed[byteIndex++] =
+ unicode[unicodeIndex] & 0x00FF;
+ unicodeIndex++;
+ }
+ }
+ return(byteIndex);
+}
+
+/*****************************************************************************/
+/*
+ * CRC 010041
+ */
+static unsigned short crc_table[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+ 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+ 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+ 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+ 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+ 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+ 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+ 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+ 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+ 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+ 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+ 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+ 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+ 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+ 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+ 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+ 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+ 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+ 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+ 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+ 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+ 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+ 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+
+unsigned short
+udf_cksum(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/fs/udf/osta.h b/sys/fs/udf/osta.h
new file mode 100644
index 0000000..5c26b6b
--- /dev/null
+++ b/sys/fs/udf/osta.h
@@ -0,0 +1,27 @@
+/*
+ * Prototypes for the OSTA functions
+ *
+ * $FreeBSD$
+ */
+
+#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_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 0000000..4942223
--- /dev/null
+++ b/sys/fs/udf/udf.h
@@ -0,0 +1,115 @@
+/*-
+ * 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$
+ */
+
+struct udf_node {
+ TAILQ_ENTRY(udf_node) tq;
+ struct vnode *i_vnode;
+ struct vnode *i_devvp;
+ struct udf_mnt *udfmp;
+ dev_t i_dev;
+ ino_t hash_id;
+ long diroff;
+ struct file_entry *fentry;
+};
+
+struct udf_mnt {
+ int im_flags;
+ struct mount *im_mountp;
+ dev_t im_dev;
+ struct vnode *im_devvp;
+ int bsize;
+ int bshift;
+ int bmask;
+ u_int32_t part_start;
+ u_int32_t part_len;
+ u_int64_t root_id;
+ struct vnode *root_vp;
+ struct long_ad root_icb;
+ TAILQ_HEAD(, udf_node) udf_tqh;
+ struct mtx hash_mtx;
+ int p_sectors;
+ int s_table_entries;
+ struct udf_sparing_table *s_table;
+};
+
+#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_readlblks(struct udf_mnt *udfmp, int sector, int size, struct buf **bp)
+{
+ return (bread(udfmp->im_devvp, sector << (udfmp->bshift - DEV_BSHIFT),
+ (size + udfmp->bmask) & ~udfmp->bmask, NOCRED, 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.
+ * 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 ino_t
+udf_getid(struct long_ad *icb)
+{
+ return (icb->loc.lb_num);
+}
+
+int udf_allocv(struct mount *, struct vnode **, struct thread *);
+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 *, u_int16_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;
diff --git a/sys/fs/udf/udf_mount.h b/sys/fs/udf/udf_mount.h
new file mode 100644
index 0000000..557f0e8
--- /dev/null
+++ b/sys/fs/udf/udf_mount.h
@@ -0,0 +1,34 @@
+/*-
+ * 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$
+ */
+
+struct udf_args {
+ char *fspec;
+ struct export_args export;
+ int flags;
+ int ssector;
+};
diff --git a/sys/fs/udf/udf_vfsops.c b/sys/fs/udf/udf_vfsops.c
new file mode 100644
index 0000000..aecb84c
--- /dev/null
+++ b/sys/fs/udf/udf_vfsops.c
@@ -0,0 +1,744 @@
+/*-
+ * 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/namei.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/fcntl.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/conf.h>
+#include <sys/queue.h>
+#include <sys/dirent.h>
+
+#include <vm/uma.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf.h>
+#include <fs/udf/udf_mount.h>
+#include <fs/udf/osta.h>
+
+MALLOC_DEFINE(M_UDFMOUNT, "UDF mount", "UDF mount structure");
+MALLOC_DEFINE(M_UDFFENTRY, "UDF fentry", "UDF file entry structure");
+MALLOC_DEFINE(M_UDFSTABLE, "UDF s_table", "UDF sparing table");
+
+/* Zones */
+uma_zone_t udf_zone_trans = NULL;
+uma_zone_t udf_zone_node = NULL;
+
+static int udf_init(struct vfsconf *);
+static int udf_uninit(struct vfsconf *);
+static int udf_mount(struct mount *, char *, caddr_t, struct nameidata *,
+ struct thread *);
+static int udf_unmount(struct mount *, int, struct thread *);
+static int udf_root(struct mount *, struct vnode **);
+static int udf_statfs(struct mount *, struct statfs *, struct thread *);
+static int udf_fhtovp(struct mount *, struct fid *, struct vnode **);
+static int udf_vptofh(struct vnode *, struct fid *);
+static int udf_find_partmaps(struct udf_mnt *, struct logvol_desc *);
+
+static struct vfsops udf_vfsops = {
+ udf_mount,
+ vfs_stdstart,
+ udf_unmount,
+ udf_root,
+ vfs_stdquotactl,
+ udf_statfs,
+ vfs_stdsync,
+ udf_vget,
+ udf_fhtovp,
+ vfs_stdcheckexp,
+ udf_vptofh,
+ udf_init,
+ udf_uninit,
+ vfs_stdextattrctl,
+};
+VFS_SET(udf_vfsops, udf, VFCF_READONLY);
+
+static int udf_mountfs(struct vnode *, struct mount *, struct thread *, struct udf_args *);
+
+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);
+
+ if ((udf_zone_node == NULL) || (udf_zone_trans == 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;
+ }
+
+ return (0);
+}
+
+static int
+udf_mount(struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, struct thread *td)
+{
+ struct vnode *devvp; /* vnode of the mount device */
+ struct udf_args args;
+ struct udf_mnt *imp = 0;
+ size_t size;
+ int error;
+
+ if ((mp->mnt_flag & MNT_RDONLY) == 0)
+ return (EROFS);
+
+ /*
+ * No root filesystem support. Probably not a big deal, since the
+ * bootloader doesn't understand UDF.
+ */
+ if (mp->mnt_flag & MNT_ROOTFS)
+ return (ENOTSUP);
+
+ if ((error = copyin(data, (caddr_t)&args, sizeof(struct udf_args))))
+ return (error);
+
+ if (mp->mnt_flag & MNT_UPDATE) {
+ imp = VFSTOUDFFS(mp);
+ if (args.fspec == 0)
+ return (vfs_export(mp, &args.export));
+ }
+
+ /* Check that the mount device exists */
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, td);
+ if ((error = namei(ndp)))
+ return (error);
+ NDFREE(ndp, NDF_ONLY_PNBUF);
+ devvp = ndp->ni_vp;
+
+ if (vn_isdisk(devvp, &error) == 0) {
+ vrele(devvp);
+ return (error);
+ }
+
+ /* Check the access rights on the mount device */
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
+ error = VOP_ACCESS(devvp, VREAD, td->td_ucred, td);
+ if (error)
+ error = suser(td);
+ if (error) {
+ vput(devvp);
+ return (error);
+ }
+ VOP_UNLOCK(devvp, 0, td);
+
+ if ((error = udf_mountfs(devvp, mp, td, &args))) {
+ vrele(devvp);
+ return (error);
+ }
+
+ imp = VFSTOUDFFS(mp);
+ copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ udf_statfs(mp, &mp->mnt_stat, td);
+ 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, u_int16_t id)
+{
+ u_int8_t *itag;
+ u_int8_t i, cksum = 0;
+
+ itag = (u_int8_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);
+}
+
+static int
+udf_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td, struct udf_args *argp) {
+ 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;
+ u_int32_t sector, size, mvds_start, mvds_end;
+ u_int32_t fsd_offset = 0;
+ u_int16_t part_num = 0, fsd_part = 0;
+ int error = EINVAL, needclose = 0;
+ int logvol_found = 0, part_found = 0, fsd_found = 0;
+ int bsize;
+
+ /*
+ * Disallow multiple mounts of the same device. Flush the buffer
+ * cache for the device.
+ */
+ if ((error = vfs_mountedon(devvp)))
+ return (error);
+ if (vcount(devvp) > 1)
+ return (EBUSY);
+ if ((error = vinvalbuf(devvp, V_SAVE, td->td_ucred, td, 0, 0)))
+ return (error);
+
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
+ error = VOP_OPEN(devvp, FREAD, FSCRED, td);
+ VOP_UNLOCK(devvp, 0, td);
+ if (error)
+ return error;
+ needclose = 1;
+
+ MALLOC(udfmp, struct udf_mnt *, 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 = (qaddr_t)udfmp;
+ mp->mnt_stat.f_fsid.val[0] = dev2udev(devvp->v_rdev);
+ mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
+ mp->mnt_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 it's 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 = avdp.main_vds_ex.loc;
+ mvds_end = mvds_start + (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 = lvd->lb_size;
+ udfmp->bmask = udfmp->bsize - 1;
+ udfmp->bshift = ffs(udfmp->bsize) - 1;
+ fsd_part = lvd->_lvd_use.fsd_loc.loc.part_num;
+ fsd_offset = 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 = pd->part_num;
+ udfmp->part_len = pd->part_len;
+ udfmp->part_start = 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 = udfmp->root_icb.loc.lb_num + udfmp->part_start;
+ size = 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;
+
+ TAILQ_INIT(&udfmp->udf_tqh);
+ devvp->v_rdev->si_mountpoint = mp;
+
+ mtx_init(&udfmp->hash_mtx, "udf_hash", NULL, MTX_DEF);
+ return 0;
+
+bail:
+ if (udfmp != NULL)
+ FREE(udfmp, M_UDFMOUNT);
+ if (bp != NULL)
+ brelse(bp);
+ if (needclose)
+ VOP_CLOSE(devvp, FREAD, NOCRED, td);
+ return error;
+};
+
+static int
+udf_unmount(struct mount *mp, int mntflags, struct thread *td)
+{
+ struct udf_mnt *udfmp;
+ int error, flags = 0;
+
+ udfmp = VFSTOUDFFS(mp);
+
+ if (mntflags & MNT_FORCE)
+ flags |= FORCECLOSE;
+
+ if ((error = vflush(mp, 0, flags)))
+ return (error);
+
+ udfmp->im_devvp->v_rdev->si_mountpoint = NULL;
+ error = VOP_CLOSE(udfmp->im_devvp, FREAD, NOCRED, td);
+ vrele(udfmp->im_devvp);
+
+ if (udfmp->s_table != NULL)
+ FREE(udfmp->s_table, M_UDFSTABLE);
+ FREE(udfmp, M_UDFMOUNT);
+
+ mp->mnt_data = (qaddr_t)0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+
+ return (0);
+}
+
+static 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, LK_EXCLUSIVE, vpp);
+ if (error)
+ return error;
+
+ vp = *vpp;
+ vp->v_flag |= VROOT;
+ udfmp->root_vp = vp;
+
+ return (0);
+}
+
+static int
+udf_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
+{
+ 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;
+ if (sbp != &mp->mnt_stat) {
+ sbp->f_type = mp->mnt_vfc->vfc_typenum;
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+
+ 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;
+
+ td = curthread;
+ udfmp = VFSTOUDFFS(mp);
+
+ /* See if we already have this in the cache */
+ if ((error = udf_hashlookup(udfmp, ino, flags, 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 = uma_zalloc(udf_zone_node, M_WAITOK);
+ if (unode == NULL) {
+ printf("Cannot allocate udf node\n");
+ return (ENOMEM);
+ }
+
+ /*
+ * 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);
+ uma_zfree(udf_zone_node, unode);
+ return (error);
+ }
+
+ fe = (struct file_entry *)bp->b_data;
+ if (udf_checktag(&fe->tag, TAGID_FENTRY)) {
+ printf("Invalid file entry!\n");
+ uma_zfree(udf_zone_node, unode);
+ brelse(bp);
+ return (ENOMEM);
+ }
+ size = UDF_FENTRY_SIZE + fe->l_ea + fe->l_ad;
+ MALLOC(unode->fentry, struct file_entry *, size, M_UDFFENTRY,
+ M_NOWAIT | M_ZERO);
+ if (unode->fentry == NULL) {
+ printf("Cannot allocate file entry block\n");
+ uma_zfree(udf_zone_node, unode);
+ brelse(bp);
+ return (ENOMEM);
+ }
+
+ bcopy(bp->b_data, unode->fentry, size);
+
+ brelse(bp);
+ bp = NULL;
+
+ 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->i_devvp = udfmp->im_devvp;
+ unode->i_dev = udfmp->im_dev;
+ unode->udfmp = udfmp;
+ vp->v_data = unode;
+ lockinit(&vp->v_lock, PINOD, "udfnode", 0, 0);
+ vp->v_vnlock = &vp->v_lock;
+ VREF(udfmp->im_devvp);
+ 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 {
+ ushort ifid_len;
+ ushort ifid_pad;
+ int ifid_ino;
+ long ifid_start;
+};
+
+static 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, LK_EXCLUSIVE, &nvp)) != 0) {
+ *vpp = NULLVP;
+ return (error);
+ }
+
+ *vpp = nvp;
+ return (0);
+}
+
+static 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);
+}
+
+static 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 < 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 *, pms->st_size,
+ M_UDFSTABLE, 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 = pms->packet_len / udfmp->bsize;
+
+ /*
+ * XXX If reading the first Sparing Table fails, should look
+ * for another table.
+ */
+ if ((error = udf_readlblks(udfmp, pms->st_loc[0], pms->st_size,
+ &bp)) != 0) {
+ printf("Failed to read Sparing Table at sector %d\n",
+ pms->st_loc[0]);
+ return (error);
+ }
+ bcopy(bp->b_data, udfmp->s_table, 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 < udfmp->s_table->rt_l; i++) {
+ udfmp->s_table_entries = i;
+ if (udfmp->s_table->entries[i].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 0000000..bb0d1aa
--- /dev/null
+++ b/sys/fs/udf/udf_vnops.c
@@ -0,0 +1,1232 @@
+/*-
+ * 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/buf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/dirent.h>
+#include <sys/queue.h>
+#include <sys/unistd.h>
+
+#include <vm/uma.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/osta.h>
+#include <fs/udf/udf.h>
+
+static int udf_access(struct vop_access_args *);
+static int udf_getattr(struct vop_getattr_args *);
+static int udf_ioctl(struct vop_ioctl_args *);
+static int udf_pathconf(struct vop_pathconf_args *);
+static int udf_read(struct vop_read_args *);
+static int udf_readdir(struct vop_readdir_args *);
+static int udf_readlink(struct vop_readlink_args *ap);
+static int udf_strategy(struct vop_strategy_args *);
+static int udf_print(struct vop_print_args *);
+static int udf_bmap(struct vop_bmap_args *);
+static int udf_lookup(struct vop_cachedlookup_args *);
+static int udf_reclaim(struct vop_reclaim_args *);
+static void udf_dumpblock(void *, int) __unused;
+static int udf_readatoffset(struct udf_node *, int *, int, struct buf **, u_int8_t **);
+static int udf_bmap_internal(struct udf_node *, u_int32_t, daddr64_t *, u_int32_t *);
+
+vop_t **udf_vnodeop_p;
+static struct vnodeopv_entry_desc udf_vnodeop_entries[] = {
+ { &vop_default_desc, (vop_t *) vop_defaultop },
+ { &vop_access_desc, (vop_t *) udf_access },
+ { &vop_bmap_desc, (vop_t *) udf_bmap },
+ { &vop_cachedlookup_desc, (vop_t *) udf_lookup },
+ { &vop_getattr_desc, (vop_t *) udf_getattr },
+ { &vop_inactive_desc, (vop_t *) vop_stdinactive },
+ { &vop_ioctl_desc, (vop_t *) udf_ioctl },
+ { &vop_islocked_desc, (vop_t *) vop_stdislocked },
+ { &vop_lock_desc, (vop_t *) vop_stdlock },
+ { &vop_lookup_desc, (vop_t *) vfs_cache_lookup },
+ { &vop_pathconf_desc, (vop_t *) udf_pathconf },
+ { &vop_print_desc, (vop_t *) udf_print },
+ { &vop_read_desc, (vop_t *) udf_read },
+ { &vop_readdir_desc, (vop_t *) udf_readdir },
+ { &vop_readlink_desc, (vop_t *) udf_readlink },
+ { &vop_reclaim_desc, (vop_t *) udf_reclaim },
+ { &vop_strategy_desc, (vop_t *) udf_strategy },
+ { &vop_unlock_desc, (vop_t *) vop_stdunlock },
+ { &vop_getpages_desc, (vop_t *) vop_stdgetpages },
+ { &vop_putpages_desc, (vop_t *) vop_stdputpages },
+ { NULL, NULL }
+};
+static struct vnodeopv_desc udf_vnodeop_opv_desc =
+ { &udf_vnodeop_p, udf_vnodeop_entries };
+VNODEOP_SET(udf_vnodeop_opv_desc);
+
+MALLOC_DEFINE(M_UDFFID, "UDF FID", "UDF FileId structure");
+
+/* 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;
+ int error;
+
+ *vpp = NULL;
+
+loop:
+ mtx_lock(&udfmp->hash_mtx);
+ TAILQ_FOREACH(node, &udfmp->udf_tqh, tq) {
+ if (node->hash_id == id) {
+ VI_LOCK(node->i_vnode);
+ mtx_unlock(&udfmp->hash_mtx);
+ error = vget(node->i_vnode, flags | LK_INTERLOCK,
+ curthread);
+ if (error == ENOENT)
+ goto loop;
+ if (error)
+ return (error);
+ *vpp = node->i_vnode;
+ return (0);
+ }
+ }
+
+ mtx_unlock(&udfmp->hash_mtx);
+ return (0);
+}
+
+int
+udf_hashins(struct udf_node *node)
+{
+ struct udf_mnt *udfmp;
+
+ udfmp = node->udfmp;
+
+ mtx_lock(&udfmp->hash_mtx);
+ TAILQ_INSERT_TAIL(&udfmp->udf_tqh, node, tq);
+ mtx_unlock(&udfmp->hash_mtx);
+ lockmgr(&node->i_vnode->v_lock, LK_EXCLUSIVE, (struct mtx *)0,
+ curthread);
+
+ return (0);
+}
+
+int
+udf_hashrem(struct udf_node *node)
+{
+ struct udf_mnt *udfmp;
+
+ udfmp = node->udfmp;
+
+ mtx_lock(&udfmp->hash_mtx);
+ TAILQ_REMOVE(&udfmp->udf_tqh, node, tq);
+ mtx_unlock(&udfmp->hash_mtx);
+
+ return (0);
+}
+
+int
+udf_allocv(struct mount *mp, struct vnode **vpp, struct thread *td)
+{
+ 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);
+ }
+
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ *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)
+{
+ u_int32_t perm;
+ u_int32_t flags;
+ mode_t mode;
+
+ perm = node->fentry->perm;
+ flags = 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;
+ mode_t a_mode, mode;
+
+ vp = a->a_vp;
+ node = VTON(vp);
+ a_mode = a->a_mode;
+
+ if (a_mode & 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,
+ a_mode, a->a_cred, NULL));
+}
+
+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;
+ union {
+ u_int16_t u_tz_offset;
+ int16_t s_tz_offset;
+ } tz;
+
+ t->tv_nsec = 0;
+
+ /* DirectCD seems to like using bogus year values */
+ if (time->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(time->year);
+ for (i = 1; i < time->month; i++)
+ t->tv_sec += mon_lens[lpyear][i] * 3600 * 24;
+
+ /* Speed up the calculation */
+ if (time->year > 1979)
+ t->tv_sec += 315532800;
+ if (time->year > 1989)
+ t->tv_sec += 315619200;
+ if (time->year > 1999)
+ t->tv_sec += 315532800;
+ for (i = 2000; i < time->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 = 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;
+}
+
+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->i_dev);
+ vap->va_fileid = node->hash_id;
+ vap->va_mode = udf_permtomode(node);
+ vap->va_nlink = 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 = (fentry->uid == -1) ? 0 : fentry->uid;
+ vap->va_gid = (fentry->gid == -1) ? 0 : 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 = fentry->logblks_rec * node->udfmp->bsize;
+ } else {
+ vap->va_size = node->udfmp->bsize;
+ }
+ } else {
+ vap->va_size = fentry->inf_len;
+ }
+ vap->va_flags = 0;
+ vap->va_gen = 1;
+ vap->va_blocksize = node->udfmp->bsize;
+ vap->va_bytes = fentry->inf_len;
+ vap->va_type = vp->v_type;
+ vap->va_filerev = 0; /* XXX */
+ return (0);
+}
+
+/*
+ * File specific ioctls. DeCSS candidate?
+ */
+static int
+udf_ioctl(struct vop_ioctl_args *a)
+{
+ printf("%s called\n", __FUNCTION__);
+ return (EOPNOTSUPP);
+}
+
+/*
+ * 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_LINK_MAX:
+ *a->a_retval = 65535;
+ return (0);
+ case _PC_NAME_MAX:
+ *a->a_retval = NAME_MAX;
+ return (0);
+ case _PC_PATH_MAX:
+ *a->a_retval = PATH_MAX;
+ return (0);
+ case _PC_NO_TRUNC:
+ *a->a_retval = 1;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+}
+
+static int
+udf_read(struct vop_read_args *a)
+{
+ struct vnode *vp = a->a_vp;
+ struct uio *uio = a->a_uio;
+ struct udf_node *node = VTON(vp);
+ struct buf *bp;
+ u_int8_t *data;
+ int error = 0;
+ int size, n, fsize, offset;
+
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+
+ fsize = node->fentry->inf_len;
+ size = 0;
+ while (uio->uio_offset < fsize && uio->uio_resid > 0) {
+ offset = uio->uio_offset;
+ error = udf_readatoffset(node, &size, offset, &bp, &data);
+ if (error)
+ return (error);
+ n = min(size, size - bp->b_resid);
+ error = uiomove((caddr_t)data, n, uio);
+ if (bp != NULL)
+ brelse(bp);
+ size -= n;
+ if (error)
+ break;
+ };
+
+ return (error);
+}
+
+/* Convienience routine to dump a block in hex */
+static void
+udf_dumpblock(void *data, int len)
+{
+ int i, j;
+
+ for (i = 0; i < len; i++) {
+ printf("\noffset= %d: ", i);
+ for (j = 0; j < 8; j++) {
+ if (i + j == len)
+ break;
+ printf("0x%02x ", (u_int8_t)((u_int8_t*)(data))[i + j]);
+ }
+ i += j - 1;
+ }
+ printf("\n");
+}
+
+/*
+ * 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. For now, compact
+ * the encoding to 8 bits if possible. Return the length of the translated
+ * string.
+ * XXX This horribly pessimizes the 8bit case
+ */
+static int
+udf_transname(char *cs0string, char *destname, int len)
+{
+ unicode_t *transname;
+ int i, unilen = 0;
+
+ /* allocate a buffer big enough to hold an 8->16 bit expansion */
+ transname = uma_zalloc(udf_zone_trans, M_WAITOK);
+ if (transname == NULL) {
+ printf("udf: out of memory?\n");
+ return 0;
+ }
+
+ if ((unilen = udf_UncompressUnicode(len, cs0string, transname)) == -1) {
+ printf("udf: Unicode translation failed\n");
+ uma_zfree(udf_zone_trans, transname);
+ return 0;
+ }
+
+ /* At this point, the name is in 16-bit Unicode. Compact it down
+ * to 8-bit
+ */
+ for (i = 0; i < unilen ; i++) {
+ if (transname[i] & 0xff00) {
+ destname[i] = '.'; /* Fudge the 16bit chars */
+ } else {
+ destname[i] = transname[i] & 0xff;
+ }
+ }
+
+ destname[unilen] = 0;
+ uma_zfree(udf_zone_trans, transname);
+
+ return unilen;
+}
+
+/*
+ * 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)
+{
+ char transname[MAXNAMLEN+1]; /* XXX stack */
+
+ if ((cs0len = udf_transname(cs0string, &transname[0], cs0len)) == 0)
+ return -1;
+
+ /* Easy check. If they aren't the same length, they aren't equal */
+ if (cs0len != cmplen)
+ return -1;
+
+ return (bcmp(transname, cmpname, cmplen));
+}
+
+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((caddr_t)uiodir->dirent, de_size, uio));
+}
+
+/* Prebuild the . and .. dirents. d_fileno will need to be filled in */
+static struct dirent udf_de_dot =
+ { 0, sizeof(struct dirent), DT_DIR, 1, "." };
+static struct dirent udf_de_dotdot =
+ { 0, sizeof(struct dirent), DT_DIR, 2, ".." };
+
+static int
+udf_readdir(struct vop_readdir_args *a)
+{
+ struct vnode *vp;
+ struct buf *bp;
+ struct uio *uio;
+ struct dirent dir;
+ struct udf_node *node;
+ struct udf_mnt *udfmp;
+ struct fileid_desc *fid;
+ struct udf_uiodir uiodir;
+ u_long *cookies = NULL;
+ u_int8_t *data;
+ int ncookies;
+ int error = 0, offset, off, size, de_size, fid_size, fsize;
+ int total_fid_size = 0, frag_size = 0, fid_fragment = 0;
+
+ vp = a->a_vp;
+ uio = a->a_uio;
+ node = VTON(vp);
+ udfmp = node->udfmp;
+ de_size = sizeof(struct dirent);
+ fid_size = UDF_FID_SIZE;
+ fsize = node->fentry->inf_len;
+ 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;
+ 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;
+ }
+
+ /*
+ * offset is the absolute offset into the file data. off is the offset
+ * into the data, minus the blocks that weren't read because they fell
+ * before offset.
+ */
+ offset = uio->uio_offset;
+ off = 0;
+
+ /*
+ * Iterate through the file id descriptors. Give the parent dir
+ * entry special attention. size will be the size of the extent
+ * returned in data. If there is more than one extent, things get
+ * ugly.
+ */
+ size = 0;
+ error = udf_readatoffset(node, &size, offset, &bp, &data);
+ if (error) {
+ if (a->a_ncookies != NULL)
+ FREE(cookies, M_TEMP);
+ return (error);
+ }
+
+ while (offset + off < fsize) {
+
+ fid = (struct fileid_desc*)&data[off];
+
+ /* Check to see if the fid is fragmented */
+ if (off >= size || off + fid_size > size ||
+ off + fid->l_iu + fid->l_fi + fid_size > size) {
+ struct fileid_desc *fid_buf;
+ u_int8_t *buf;
+
+ /* Copy what we have of the fid into a buffer */
+ frag_size = size - off;
+ MALLOC(buf, u_int8_t*, max(frag_size, fid_size),
+ M_UDFFID, M_NOWAIT | M_ZERO);
+ if (buf == NULL)
+ panic("No memory?");
+ bcopy(fid, buf, frag_size);
+
+ /* Reduce all of the casting magic */
+ fid_buf = (struct fileid_desc*)buf;
+
+ if (bp != NULL)
+ brelse(bp);
+
+ /* Fetch the next allocation */
+ offset += size;
+ size = 0;
+ error = udf_readatoffset(node, &size, offset, &bp,
+ &data);
+ if (error)
+ break;
+
+ /*
+ * If the fragment was so small that we didn't get
+ * the l_iu and l_fi fields, copy those in.
+ */
+ if (fid_size > frag_size)
+ bcopy(data, &buf[frag_size],
+ fid_size - frag_size);
+
+ /*
+ * Now that we have enough of the fid to work with,
+ * allocate a new fid, copy the fragment into it,
+ * and copy the rest of the fid from the new
+ * allocation.
+ */
+ total_fid_size = fid_size + fid_buf->l_iu +
+ fid_buf->l_fi;
+ MALLOC(fid, struct fileid_desc *, total_fid_size,
+ M_UDFFID, M_NOWAIT | M_ZERO);
+ if (fid == NULL) {
+ brelse(bp);
+ error = ENOMEM;
+ break;
+ }
+ bcopy(fid_buf, fid, frag_size);
+ bcopy(data, &((u_int8_t*)(fid))[frag_size],
+ total_fid_size - frag_size);
+
+ fid_fragment = 1;
+ FREE(buf, M_UDFFID);
+ } else {
+ total_fid_size = fid->l_iu + fid->l_fi + fid_size;
+ }
+
+ /* XXX Should we return an error on a bad fid? */
+ if (udf_checktag(&fid->tag, TAGID_FID)) {
+ printf("Invalid FID tag\n");
+ break;
+ }
+
+ /* Is this a deleted file? */
+ if (fid->file_char & 0x4)
+ goto update_offset;
+
+ if (fid->l_iu != 0) {
+ printf("Possibly invalid fid found.\n");
+ goto update_offset;
+ }
+
+ if ((fid->l_fi == 0) && (fid->file_char & 0x08)) {
+ /* 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
+ * XXX Should the magic dirents be locked?
+ */
+ udf_de_dot.d_fileno = node->hash_id;
+ uiodir.dirent = &udf_de_dot;
+ error = udf_uiodir(&uiodir, de_size, uio, 1);
+ if (error)
+ break;
+
+ udf_de_dotdot.d_fileno = udf_getid(&fid->icb);
+ uiodir.dirent = &udf_de_dotdot;
+ error = udf_uiodir(&uiodir, de_size, uio, 2);
+ } else {
+ dir.d_namlen = udf_transname(&fid->data[fid->l_iu],
+ &dir.d_name[0], fid->l_fi);
+ dir.d_fileno = udf_getid(&fid->icb);
+ dir.d_type = (fid->file_char & 0x02) ? DT_DIR :
+ DT_UNKNOWN;
+ dir.d_reclen = GENERIC_DIRSIZ(&dir);
+ uiodir.dirent = &dir;
+ error = udf_uiodir(&uiodir, dir.d_reclen, uio, off);
+ }
+ if (error) {
+ printf("uiomove returned %d\n", error);
+ break;
+ }
+
+update_offset: /*
+ * Update the offset. Align on a 4 byte boundary because the
+ * UDF spec says so. If it was a fragmented entry, clean up.
+ */
+ if (fid_fragment) {
+ off = (total_fid_size - frag_size + 3) & ~0x03;
+ FREE(fid, M_UDFFID);
+ fid_fragment = 0;
+ } else {
+ off += (total_fid_size + 3) & ~0x03;
+ }
+ }
+
+ /* tell the calling layer whether we need to be called again */
+ *a->a_eofflag = uiodir.eofflag;
+ uio->uio_offset = offset + off;
+
+ if (bp != NULL)
+ brelse(bp);
+
+ if (a->a_ncookies != NULL) {
+ if (error)
+ free(cookies, M_TEMP);
+ else {
+ *a->a_ncookies = uiodir.acookies;
+ *a->a_cookies = cookies;
+ }
+ }
+
+ return (error);
+}
+
+/* Are there any implementations out there that do soft-links? */
+static int
+udf_readlink(struct vop_readlink_args *ap)
+{
+ printf("%s called\n", __FUNCTION__);
+ return (EOPNOTSUPP);
+}
+
+static int
+udf_strategy(struct vop_strategy_args *a)
+{
+ struct buf *bp;
+ struct vnode *vp;
+ struct udf_node *node;
+ int maxsize;
+
+ bp = a->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;
+ }
+ }
+ if ((long)bp->b_blkno == -1) {
+ bufdone(bp);
+ return (0);
+ }
+ vp = node->i_devvp;
+ bp->b_dev = vp->v_rdev;
+ VOP_STRATEGY(vp, bp);
+ return (0);
+}
+
+static int
+udf_print(struct vop_print_args *a)
+{
+ printf("%s called\n", __FUNCTION__);
+ return (EOPNOTSUPP);
+}
+
+static int
+udf_bmap(struct vop_bmap_args *a)
+{
+ struct udf_node *node;
+ u_int32_t max_size;
+ int error;
+
+ node = VTON(a->a_vp);
+
+ if (a->a_vpp != NULL)
+ *a->a_vpp = node->i_devvp;
+ if (a->a_bnp == NULL)
+ return (0);
+ if (a->a_runb)
+ *a->a_runb = 0;
+
+ error = udf_bmap_internal(node, a->a_bn * node->udfmp->bsize, a->a_bnp,
+ &max_size);
+ if (error > 0)
+ return (error);
+
+ /* Punt on read-ahead for now */
+ if (a->a_runp)
+ *a->a_runp = 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 buf *bp = NULL;
+ struct udf_node *node;
+ struct udf_mnt *udfmp;
+ struct fileid_desc *fid = NULL;
+ struct thread *td;
+ u_long nameiop;
+ u_long flags;
+ char *nameptr;
+ long namelen;
+ ino_t id = 0;
+ u_int8_t *data;
+ int offset, off, error, size;
+ int numdirpasses, fid_size, fsize, icb_len;
+ int total_fid_size = 0, fid_fragment = 0;
+
+ dvp = a->a_dvp;
+ node = VTON(dvp);
+ udfmp = node->udfmp;
+ nameiop = a->a_cnp->cn_nameiop;
+ flags = a->a_cnp->cn_flags;
+ nameptr = a->a_cnp->cn_nameptr;
+ namelen = a->a_cnp->cn_namelen;
+ fid_size = UDF_FID_SIZE;
+ fsize = node->fentry->inf_len;
+ icb_len = sizeof(struct long_ad);
+ td = a->a_cnp->cn_thread;
+
+ /*
+ * 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 > size) {
+ offset = 0;
+ numdirpasses = 1;
+ } else {
+ offset = node->diroff;
+ numdirpasses = 2;
+ nchstats.ncs_2passes++;
+ }
+
+ /*
+ * The name lookup algorithm is quite similar to what is in readdir.
+ * Can this be broken out and shared?
+ */
+lookloop:
+ size = 0;
+ off = 0;
+ error = udf_readatoffset(node, &size, offset, &bp, &data);
+ if (error)
+ return (error);
+
+ while (offset + off < fsize) {
+ fid = (struct fileid_desc*)&data[off];
+
+ /* Check to see if the fid is fragmented */
+ if (off >= size || off + fid_size > size ||
+ off + fid_size + fid->l_iu + fid->l_fi > size) {
+ struct fileid_desc *fid_buf;
+ u_int8_t *buf;
+ int frag_size = 0;
+
+ /* Copy what we have of the fid into a buffer */
+ frag_size = size - off;
+ MALLOC(buf, u_int8_t*, max(frag_size, fid_size),
+ M_UDFFID, M_NOWAIT | M_ZERO);
+ if (buf == NULL)
+ panic("No memory?");
+ bcopy(fid, buf, frag_size);
+
+ /* Reduce all of the casting magic */
+ fid_buf = (struct fileid_desc*)buf;
+
+ if (bp != NULL)
+ brelse(bp);
+
+ /* Fetch the next allocation */
+ offset += size;
+ size = 0;
+ error = udf_readatoffset(node, &size, offset, &bp,
+ &data);
+ if (error)
+ return (error);
+
+ /*
+ * If the fragment was so small that we didn't get
+ * the l_iu and l_fi fields, copy those in.
+ */
+ if (fid_size > frag_size)
+ bcopy(data, &buf[frag_size],
+ fid_size - frag_size);
+
+ /*
+ * Now that we have enough of the fid to work with,
+ * allocate a new fid, copy the fragment into it,
+ * and copy the rest of the fid from the new
+ * allocation.
+ */
+ total_fid_size = fid_size + fid_buf->l_iu +
+ fid_buf->l_fi;
+ MALLOC(fid, struct fileid_desc *, total_fid_size,
+ M_UDFFID, M_NOWAIT | M_ZERO);
+ if (fid == NULL) {
+ brelse(bp);
+ return (ENOMEM);
+ }
+ bcopy(fid_buf, fid, frag_size);
+ bcopy(data, &((u_int8_t*)(fid))[frag_size],
+ total_fid_size - frag_size);
+
+ off = (total_fid_size - frag_size + 3) & ~0x03;
+ fid_fragment = 1;
+ FREE(buf, M_UDFFID);
+ } else {
+ /*
+ * Update the offset here to avoid looking at this fid
+ * again on a subsequent lookup.
+ */
+ total_fid_size = fid->l_iu + fid->l_fi + fid_size;
+ off += (total_fid_size + 3) & ~0x03;
+ }
+
+ /* XXX Should we return an error on a bad fid? */
+ if (udf_checktag(&fid->tag, TAGID_FID))
+ break;
+
+ if ((fid->l_fi == 0) && (fid->file_char & 0x08)) {
+ if (flags & ISDOTDOT) {
+ id = udf_getid(&fid->icb);
+ break;
+ }
+ } else {
+ if (!(udf_cmpname(&fid->data[fid->l_iu],
+ nameptr, fid->l_fi, namelen))) {
+ id = udf_getid(&fid->icb);
+ break;
+ }
+ }
+
+ /*
+ * If we got this far then this fid isn't what we were
+ * looking for. It's therefore safe to clean up from a
+ * fragmented fid.
+ */
+ if (fid_fragment) {
+ FREE(fid, M_UDFFID);
+ fid_fragment = 0;
+ }
+ }
+
+ /* Did we have a match? */
+ if (id) {
+ error = udf_vget(udfmp->im_mountp, id, LK_EXCLUSIVE, &tdp);
+ if (bp != NULL)
+ brelse(bp);
+ if (error)
+ return (error);
+
+ /* Remember where this entry was if it's the final component */
+ if ((flags & ISLASTCN) && nameiop == LOOKUP)
+ node->diroff = offset + off;
+ if (numdirpasses == 2)
+ nchstats.ncs_pass2++;
+ if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) {
+ a->a_cnp->cn_flags |= PDIRUNLOCK;
+ VOP_UNLOCK(dvp, 0, td);
+ }
+
+ *vpp = tdp;
+
+ /* Put this entry in the cache */
+ if (flags & MAKEENTRY)
+ cache_enter(dvp, *vpp, a->a_cnp);
+
+ if (fid_fragment)
+ FREE(fid, M_UDFFID);
+
+ return (0);
+ }
+
+ /* Name wasn't found on this pass. Do another pass? */
+ if (numdirpasses == 2) {
+ numdirpasses--;
+ offset = 0;
+ goto lookloop;
+ }
+
+ if (bp != NULL)
+ brelse(bp);
+
+ /* Enter name into cache as non-existant */
+ if (flags & MAKEENTRY)
+ cache_enter(dvp, *vpp, a->a_cnp);
+
+ /* Why wait to the very end to decide that this is a read-only fs? */
+ if (nameiop == CREATE || nameiop == RENAME)
+ return (EROFS);
+ return (ENOENT);
+
+}
+
+static int
+udf_reclaim(struct vop_reclaim_args *a)
+{
+ struct vnode *vp;
+ struct udf_node *unode;
+
+ vp = a->a_vp;
+ unode = VTON(vp);
+
+ cache_purge(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);
+ lockdestroy(&unode->i_vnode->v_lock);
+ uma_zfree(udf_zone_node, 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.
+ * XXX 'size' is limited to the logical block size for now due to problems
+ * with udf_read()
+ */
+static int
+udf_readatoffset(struct udf_node *node, int *size, int offset, struct buf **bp, u_int8_t **data)
+{
+ struct udf_mnt *udfmp;
+ struct file_entry *fentry = NULL;
+ struct buf *bp1;
+ u_int32_t max_size;
+ daddr64_t sector;
+ int error;
+
+ udfmp = node->udfmp;
+
+ error = udf_bmap_internal(node, offset, &sector, &max_size);
+ if (error == -1) {
+ /*
+ * This error means that the file *data* is stored in the
+ * allocation descriptor field of the file entry.
+ */
+ fentry = node->fentry;
+ *data = &fentry->data[fentry->l_ea];
+ *size = fentry->l_ad;
+ *bp = NULL;
+ return (0);
+ } else if (error != 0) {
+ return (error);
+ }
+
+ if (*size == 0 || *size > max_size)
+ *size = max_size;
+
+ /* XXX Read only one block at a time? Could read-ahead help? */
+ *size = min(*size, udfmp->bsize);
+ if (*size == 0)
+ return (EIO);
+
+ if ((error = udf_readlblks(udfmp, sector, *size, bp))) {
+ printf("udf_readlblks returned %d\n", error);
+ return (error);
+ }
+
+ bp1 = *bp;
+ *data = (u_int8_t *)&bp1->b_data[offset % udfmp->bsize];
+ return (0);
+}
+
+/*
+ * Translate a file offset into a logical block and then into a physical
+ * block.
+ */
+static int
+udf_bmap_internal(struct udf_node *node, u_int32_t offset, daddr64_t *sector, u_int32_t *max_size)
+{
+ struct udf_mnt *udfmp;
+ struct file_entry *fentry;
+ void *icb;
+ struct icb_tag *tag;
+ u_int32_t icblen = 0;
+ daddr64_t lsector;
+ int ad_offset, ad_num = 0;
+ int i, p_offset;
+
+ udfmp = node->udfmp;
+ fentry = node->fentry;
+ tag = &fentry->icbtag;
+
+ switch (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 (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 > fentry->l_ad) {
+ printf("File offset out of bounds\n");
+ return (EINVAL);
+ }
+ icb = GETICB(long_ad, fentry, 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) - 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 > fentry->l_ad) {
+ printf("File offset out of bounds\n");
+ return (EINVAL);
+ }
+ icb = GETICB(long_ad, fentry, fentry->l_ea + ad_offset);
+ icblen = GETICBLEN(long_ad, icb);
+ ad_num++;
+ } while(offset >= icblen);
+
+ lsector = (offset >> udfmp->bshift) +
+ ((struct long_ad *)(icb))->loc.lb_num;
+
+ *max_size = GETICBLEN(long_ad, icb) - 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->bsize;
+
+ return (-1);
+ 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 - udfmp->s_table->entries[i].org;
+ if ((p_offset < udfmp->p_sectors) && (p_offset >= 0)) {
+ *sector = udfmp->s_table->entries[i].map +
+ p_offset;
+ break;
+ }
+ }
+ }
+
+ return (0);
+}
OpenPOWER on IntegriCloud