hsfs_node.c revision d10b670251155449ab8d25790cb23de3995cca8e
2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License (the "License"). 2N/A * You may not use this file except in compliance with the License. 2N/A * See the License for the specific language governing permissions 2N/A * and limitations under the License. 2N/A * When distributing Covered Code, include this CDDL HEADER in each 2N/A * If applicable, add the following below this CDDL HEADER, with the 2N/A * fields enclosed by brackets "[]" replaced with your own identifying 2N/A * information: Portions Copyright [yyyy] [name of copyright owner] 2N/A * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 2N/A * Use is subject to license terms. 2N/A#
pragma ident "%Z%%M% %I% %E% SMI" 2N/A * Directory operations for High Sierra filesystem 2N/A * This macro expects a name that ends in '.' and returns TRUE if the 2N/A * name is not "." or ".." 2N/A * These values determine whether we will try to read a file or dir; 2N/A * they may be patched via /etc/system to allow users to read 2N/A * record-oriented files. 2N/A * This variable determines if the HSFS code will use the 2N/A * directory name lookup cache. The default is for the cache to be used. 2N/A * This variable determines whether strict ISO-9660 directory ordering 2N/A * is to be assumed. If false (which it is by default), then when 2N/A * searching a directory of an ISO-9660 disk, we do not expect the 2N/A * entries to be sorted (as the spec requires), and so cannot terminate 2N/A * the search early. Unfortunately, some vendors are producing 2N/A * non-compliant disks. This variable exists to revert to the old 2N/A * behavior in case someone relies on this. This option is expected to be 2N/A * removed at some point in the future. 2N/A * Use "set hsfs:strict_iso9660_ordering = 1" in /etc/system to override. 2N/A * This tunable allows us to ignore inode numbers from rrip-1.12. 2N/A * In this case, we fall back to our default inode algorithm. 2N/A * Return 0 if the desired access may be granted. 2N/A * Otherwise return error code. 2N/A * Write access cannot be granted for a read-only medium 2N/A * XXX - For now, use volume protections. 2N/A * Also, always grant EXEC access for directories 2N/A * if READ access is granted. 2N/A * The tunable nhsnode is now a threshold for a dynamically allocated 2N/A * pool of hsnodes, not the size of a statically allocated table. 2N/A * When the number of hsnodes for a particular file system exceeds 2N/A * nhsnode, the allocate and free logic will try to reduce the number 2N/A * of allocated nodes by returning unreferenced nodes to the kmem_cache 2N/A * instead of putting them on the file system's private free list. 2N/A * Initialize the cache of free hsnodes. 2N/A * A kmem_cache is used for the hsnodes 2N/A * No constructor because hsnodes are initialised by bzeroing. 2N/A * Destroy the cache of free hsnodes. 2N/A * System is short on memory, free up as much as possible 2N/A * For each vfs in the hs_mounttab list 2N/A * Purge the dnlc of all hsfs entries 2N/A * For each entry in the free chain 2N/A * Free the node. Force it to be fully freed 2N/A * by setting the 3rd arg (nopage) to 1. 2N/A * Add an hsnode to the end of the free list. 2N/A * Get an hsnode from the front of the free list. 2N/A * Must be called with write hsfs_hash_lock held. 2N/A * If the number of currently-allocated hsnodes is less than 2N/A * the hsnode count threshold (nhsnode), or if there are no 2N/A * nodes on the file system's local free list (which acts as a 2N/A * cache), call kmem_cache_alloc to get a new hsnode from 2N/A /* hp cannot be NULL, since we already checked this above */ 2N/A * file is no longer referenced, destroy all old pages 2N/A * pvn_vplist_dirty will abort all old pages 2N/A * Remove an hsnode from the free list. 2N/A * Look for hsnode in hash list. 2N/A * If the inode number is != HS_DUMMY_INO (16), then only the inode 2N/A * number is used for the check. 2N/A * If the inode number is == HS_DUMMY_INO, we additionally always 2N/A * check the directory offset for the file to avoid caching the 2N/A * meta data for all zero sized to the first zero sized file that 2N/A * If found, reactivate it if inactive. 2N/A * Must be entered with hsfs_hash_lock held. 2N/A * If this is the dummy inode number, look for 2N/A * matching dir_lbn and dir_off. 2N/A * reactivating a free hsnode: 2N/A * remove from free list 2N/A * Destroy all old pages and free the hsnodes 2N/A * Return 1 if busy (a hsnode is still referenced). 2N/A /* make sure no one can come in */ 2N/A /* now free the hsnodes */ 2N/A * We know there are no pages associated with 2N/A * all the hsnodes (they've all been released 2N/A * above). So remove from free list and 2N/A * free the entry with nopage set. 2N/A /* release the root hsnode, this should free the final hsnode */ 2N/A * Construct an hsnode. 2N/A * Caller specifies the directory entry, the block number and offset 2N/A * of the directory entry, and the vfs pointer. 2N/A * note: off is the sector offset, not lbn offset 2N/A * if NULL is returned implies file system hsnode table full 2N/A * Construct the data that allows us to re-read the meta data without 2N/A * knowing the name of the file: in the case of a directory 2N/A * entry, this should point to the canonical dirent, the "." 2N/A * directory entry for the directory. This dirent is pointed 2N/A * to by all directory entries for that dir (including the ".") 2N/A * In the case of a file, simply point to the dirent for that 2N/A * file (there are hard links in Rock Ridge, so we need to use 2N/A * different data to contruct the node id). 2N/A * Normalize lbn and off before creating a nodeid 2N/A * and before storing them in a hs_node structure 2N/A * If the media carries rrip-v1.12 or newer, and we trust the inodes 2N/A * from the rrip data (use_rrip_inodes != 0), use that data. If the 2N/A * media has been created by a recent mkisofs version, we may trust 2N/A * all numbers in the starting extent number; otherwise, we cannot 2N/A * do this for zero sized files and symlinks, because if we did we'd 2N/A * end up mapping all of them to the same node. 2N/A * We use HS_DUMMY_INO in this case and make sure that we will not 2N/A * map all files to the same meta data. 2N/A /* look for hsnode in cache first */ 2N/A * Not in cache. However, someone else may have come 2N/A * to the same conclusion and just put one in. Upgrade 2N/A * our lock to a write lock and look again. 2N/A * Now we are really sure that the hsnode is not 2N/A * in the cache. Get one off freelist or else 2N/A * allocate one. Either way get a bzeroed hsnode. 2N/A * We've just copied this pointer into hs_dirent, 2N/A * and don't want 2 references to same symlink. 2N/A * No need to hold any lock because hsnode is not 2N/A * yet in the hash chain. 2N/A * if it's a device, call specvp 2N/A "hs_makenode: specvp failed");
2N/A * Deactivate an hsnode. 2N/A * Leave it on the hash list but put it on the free list. 2N/A * If the vnode does not have any pages, release the hsnode to the 2N/A * kmem_cache using kmem_cache_free, else put in back of the free list. 2N/A * This function can be called with the hsfs_free_lock held, but only 2N/A * when the code is guaranteed to go through the path where the 2N/A * node is freed entirely, and not the path where the node could go back 2N/A * on the free list (and where the free lock would need to be acquired). 2N/A /* remove this node from the hash list, if it's there */ /* clean all old pages */ /* XXX - can we remove pages by fiat like this??? */ * Reconstruct a vnode given the location of its directory entry. * Caller specifies the the block number and offset * of the directory entry, and the vfs pointer. * Returns an error code or 0. /* Convert to sector and offset */ * Look for a given name in a given directory. * If found, construct an hsnode for it. int namlen,
/* length of 'name' */ char *
cmpname;
/* case-folded name */ * name == "^A" is illegal for ISO-9660 and Joliet as '..' is '\1' on * disk. It is no problem for Rock Ridge as RR uses '.' and '..'. * XXX It could be OK for Joliet also (because namelen == 1 is * XXX impossible for UCS-2) but then we need a better compare algorith. * For the purposes of comparing the name against dir entries, * If we don't consider a trailing dot as part of the filename, * remove it from the specified name /* make sure dirent is filled up with all info */ * No lock is needed - hs_offset is used as starting * point for searching the directory. /* found an entry, either correct or not */ * If we get here we know we didn't find it on the * first pass. If adhoc_search, then we started a * bit into the dir, and need to wrap around and * search the first entries. If not, then we started * at the beginning and didn't find it. * End of all dir blocks, didn't find entry. * If we found the entry, add it to the DNLC * If the entry is a device file (assuming we support Rock Ridge), * we enter the device vnode to the cache since that is what * That is ok since the CD-ROM is read-only, so (dvp,name) will * always point to the same device. * Parse a Directory Record into an hs_direntry structure. * High Sierra and ISO directory are almost the same * except the flag and date * Skip files with the associated bit set. * Skip files with the associated bit set. hdp->
inode = 0;
/* initialize with 0, then check rrip */ * Having this all filled in, let's see if we have any /* check interleaf size and skip factor */ /* must both be zero or non-zero */ "hsfs: interleaf size or skip factor error");
"hsfs: interleaving specified on zero length file");
* If the name changed, then the NM field for RRIP was hit and * we should not copy the name again, just return. * Fall back to the ISO name. Note that as in process_dirblock, * the on-disk filename length must be validated against ISO * limits - which, in case of RR present but no RR name found, * are NOT identical to fsp->hsfs_namemax on this filesystem. * A negative return value means that the file name * has been truncated to fsp->hsfs_namemax. * HS_VOL_TYPE_ISO && HS_VOL_TYPE_ISO_V2 * Delete trailing blanks, upper-to-lower case, add NULL terminator. * Returns the (possibly new) length. * Called from hsfs_readdir() via hs_parsedir() /* special handling for '.' and '..' */ }
else if (*
from ==
'\1') {
if (
maplc && (c >=
'A') && (c <=
'Z'))
* This is the Joliet variant of hs_namecopy() * Returns the new length. * Called from hsfs_readdir() via hs_parsedir() /* special handling for '.' and '..' */ }
else if (*
from ==
'\1') {
for (i = 0,
len = 0; i <
size; i++) {
c = (
from[i++] &
0xFF) <<
8;
* map a filename to upper case; * return 1 if found lowercase character * Called from process_dirblock() * via hsfs_lookup() -> hs_dirlook() -> process_dirblock() * to create an intermedia name from on disk file names for for (i = 0; i <
size; i++) {
if ((c >=
'a') && (c <=
'z')) {
* This is the Joliet variant of uppercase_cp() * map a UCS-2 filename to UTF-8; * Called from process_dirblock() * via hsfs_lookup() -> hs_dirlook() -> process_dirblock() * to create an intermedia name from on disk file names for /* special handling for '\0' and '\1' */ for (i = 0; i <
size; i +=
2) {
c = (*
from++ &
0xFF) <<
8;
"file name contains bad UCS-2 chacarter\n");
* Convert a UNIX-style name into its HSFS equivalent * replacing '.' and '..' with '\0' and '\1'. * Returns the (possibly new) length. * Called from hs_dirlook() and rrip_namecopy() * to create an intermediate name from the callers name from hsfs_lookup() * XXX Is the call from rrip_namecopy() OK? /* special handling for '.' and '..' */ for (i = 0; i <
size; i++) {
if ((c >=
'a') && (c <=
'z'))
* Convert a UTF-8 UNIX-style name into its UTF-8 Joliet/ISO equivalent * replacing '.' and '..' with '\0' and '\1'. * Returns the (possibly new) length. * Called from hs_dirlook() * to create an intermediate name from the callers name from hsfs_lookup() /* special handling for '.' and '..' */ for (i = 0; i <
size; i++) {
* Look through a directory block for a matching entry. * Note: this routine does an fbrelse() on the buffer passed in. struct fbuf *
fbp,
/* buffer containing dirblk */ char *
nm,
/* upcase nm to compare against */ int nmlen,
/* length of name */ int *
error)
/* return value: errno */ char *
dname;
/* name in directory entry */ * Directory Entries cannot span sectors. * Unused bytes at the end of each sector are zeroed * according to ISO9660, but we cannot rely on this * since both media failures and maliciously corrupted * media may return arbitrary values. * We therefore have to check for consistency: * The size of a directory entry must be at least * 34 bytes (the size of the directory entry metadata), * or zero (indicating the end-of-sector condition). * For a non-zero directory entry size of less than * 34 Bytes, log a warning. * In any case, skip the rest of this sector and * continue with the next. * Advance to the next sector boundary * Check the filename length in the ISO record for * plausibility and reset it to a safe value, in case * the name length byte is out of range. Since the ISO * name will be used as fallback if the rockridge name * blow the bounds and initialize dnamelen to a sensible * value within the limits of ISO9660. * In addition to that, the ISO filename is part of the * directory entry. If the filename length is too large * to fit, the record is invalid and we'll advance to * If the directory entry extends beyond the end of the * block, it must be invalid. Skip it. * If the rock ridge is implemented, then we copy the name * from the SUA area to rrip_name_str. If no Alternate * name is found, then use the uppercase NM in the * rrip_name_str char array. /* use iso name instead */ * make sure that we get rid of ';' in the dname of * an iso direntry, as we should have no knowledge * XXX This is done the wrong way: it does not take * XXX care of the fact that the version string is * XXX a decimal number in the range 1 to 32767. * Quickly screen for a non-matching entry, but not for RRIP. * This test doesn't work for lowercase vs. uppercase names. /* if we saw a lower case name we can't do this test either */ * Need to do an fbrelse() on the buffer, * as hs_makenode() may try to acquire * hs_hashlock, which may not be required * while a page is locked. * Strip trailing nulls or spaces from the name; * return adjusted length. If we find such junk, * log a non-conformant disk message. for (c =
nm +
len -
1; c >
nm; c--) {
if (*c ==
' ' || *c ==
'\0')
return ((
int)(c -
nm +
1));
if (c <
'0' || c >
'9') {
if (c <
'0' || c >
'9') {
* Take a UCS-2 character and convert * it into a utf8 character. * A 0 will be returned if the conversion fails {
0x00,
0x00,
0xC0,
0xE0,
0xF0,
0xF8,
0xFC };
* Convert the 16-bit character to a 32-bit character * By here the 16-bit character is converted * to a 32-bit wide character }
else if (
c_32 <
0x800) {
}
else if (
c_32 <
0x10000) {
}
else if (
c_32 <
0x200000) {
}
else if (
c_32 <
0x4000000) {
}
else if (
c_32 <=
0x7FFFFFFF) {
/* avoid signed overflow */