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