hsfs_node.c revision cf83459a3a773fbf09bbf2d90428884d292a6584
2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License, Version 1.0 only 2N/A * (the "License"). You may not use this file except in compliance 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 2005 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 * 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 * 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 * Check equality of fsid and nodeid. 2N/A * If found, reactivate it if inactive. 2N/A * Must be entered with hsfs_hash_lock held. 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 * Construct the nodeid: in the case of a directory * entry, this should point to the canonical dirent, the "." * directory entry for the directory. This dirent is pointed * to by all directory entries for that dir (including the ".") * In the case of a file, simply point to the dirent for that * file (there are no hard links in Rock Ridge, so there's no * need to determine what the canonical dirent is. * Normalize lbn and off before creating a nodeid * and before storing them in a hs_node structure /* look for hsnode in cache first */ * Not in cache. However, someone else may have come * to the same conclusion and just put one in. Upgrade * our lock to a write lock and look again. * Now we are really sure that the hsnode is not * in the cache. Get one off freelist or else * allocate one. Either way get a bzeroed hsnode. * We've just copied this pointer into hs_dirent, * and don't want 2 references to same symlink. * No need to hold any lock because hsnode is not * if it's a device, call specvp "hs_makenode: specvp failed");
* Leave it on the hash list but put it on the free list. * If the vnode does not have any pages, release the hsnode to the * kmem_cache using kmem_cache_free, else put in back of the free list. * This function can be called with the hsfs_free_lock held, but only * when the code is guaranteed to go through the path where the * node is freed entirely, and not the path where the node could go back * on the free list (and where the free lock would need to be acquired). /* 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 */ * 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. * 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. * Delete trailing blanks, upper-to-lower case, add NULL terminator. * Returns the (possibly new) length. /* special handling for '.' and '..' */ }
else if (*
from ==
'\1') {
if (
maplc && (c >=
'A') && (c <=
'Z'))
* map a filename to upper case; * return 1 if found lowercase character for (i = 0; i <
size; i++) {
if ((c >=
'a') && (c <=
'z')) {
* Convert a UNIX-style name into its HSFS equivalent. * Returns the (possibly new) length. /* special handling for '.' and '..' */ for (i = 0; i <
size; i++) {
if ((c >=
'a') && (c <=
'z'))
* 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 */ int is_rrip)
/* 1 if rock ridge is implemented */ 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 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 (
dname[i] !=
';') && (i > 0);
* 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. * Compare the names, returning < 0 if a < b, * 0 if a == b, and > 0 if a > b. /* if file version, stop */ if (!
is_rrip && ((*a ==
';') && (*b ==
'\0')))
* 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));