1/* 2 * Copyright (c) 2000-2002, 2004-2005 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28#include <sys/param.h> 29#include <sys/utfconv.h> 30#include <sys/stat.h> 31#include <sys/kernel.h> 32#include <sys/malloc.h> 33#include <libkern/libkern.h> 34 35#include "../headers/FileMgrInternal.h" 36#include "../headers/BTreesInternal.h" 37#include "../headers/CatalogPrivate.h" 38#include "../headers/HFSUnicodeWrappers.h" 39#include "../headers/BTreesPrivate.h" 40#include <string.h> 41 42// 43// Routine: LocateCatalogNodeByKey 44// 45// Function: Locates the catalog record for an existing folder or file 46// CNode and returns the key and data records. 47// 48 49OSErr 50LocateCatalogNodeByKey(const ExtendedVCB *volume, u_int32_t hint, CatalogKey *keyPtr, 51 CatalogRecord *dataPtr, u_int32_t *newHint) 52{ 53 OSErr result; 54 CatalogName *nodeName = NULL; 55 HFSCatalogNodeID threadParentID; 56 u_int16_t tempSize; 57 FSBufferDescriptor btRecord; 58 struct BTreeIterator *searchIterator; 59 FCB *fcb; 60 61 MALLOC (searchIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); 62 if (searchIterator == NULL) { 63 return memFullErr; // translates to ENOMEM 64 } 65 66 bzero(searchIterator, sizeof(*searchIterator)); 67 68 fcb = GetFileControlBlock(volume->catalogRefNum); 69 70 btRecord.bufferAddress = dataPtr; 71 btRecord.itemCount = 1; 72 btRecord.itemSize = sizeof(CatalogRecord); 73 74 searchIterator->hint.nodeNum = hint; 75 76 bcopy(keyPtr, &searchIterator->key, sizeof(CatalogKey)); 77 78 result = BTSearchRecord( fcb, searchIterator, &btRecord, &tempSize, searchIterator ); 79 80 if (result == noErr) 81 { 82 *newHint = searchIterator->hint.nodeNum; 83 84 BlockMoveData(&searchIterator->key, keyPtr, sizeof(CatalogKey)); 85 } 86 87 if (result == btNotFound) { 88 result = cmNotFound; 89 } 90 91 if (result) { 92 FREE(searchIterator, M_TEMP); 93 return result; 94 } 95 96 // if we got a thread record, then go look up real record 97 switch ( dataPtr->recordType ) 98 { 99 case kHFSFileThreadRecord: 100 case kHFSFolderThreadRecord: 101 threadParentID = dataPtr->hfsThread.parentID; 102 nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName; 103 break; 104 105 case kHFSPlusFileThreadRecord: 106 case kHFSPlusFolderThreadRecord: 107 threadParentID = dataPtr->hfsPlusThread.parentID; 108 nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName; 109 break; 110 111 default: 112 threadParentID = 0; 113 break; 114 } 115 116 if ( threadParentID ) // found a thread 117 result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint); 118 119 FREE (searchIterator, M_TEMP); 120 return result; 121} 122 123 124 125//******************************************************************************* 126// Routine: LocateCatalogRecord 127// 128// Function: Locates the catalog record associated with folderID and name 129// 130//******************************************************************************* 131 132OSErr 133LocateCatalogRecord(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name, 134 __unused u_int32_t hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, u_int32_t *newHint) 135{ 136 OSErr result; 137 uint16_t tempSize; 138 FSBufferDescriptor btRecord; 139 struct BTreeIterator *searchIterator = NULL; 140 FCB *fcb; 141 BTreeControlBlock *btcb; 142 143 MALLOC (searchIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); 144 if (searchIterator == NULL) { 145 return memFullErr; // translates to ENOMEM 146 } 147 148 bzero(searchIterator, sizeof(*searchIterator)); 149 150 151 fcb = GetFileControlBlock(volume->catalogRefNum); 152 btcb = (BTreeControlBlock *)fcb->fcbBTCBPtr; 153 154 btRecord.bufferAddress = dataPtr; 155 btRecord.itemCount = 1; 156 btRecord.itemSize = sizeof(CatalogRecord); 157 158 BuildCatalogKey(folderID, name, (volume->vcbSigWord == kHFSPlusSigWord), (CatalogKey *)&searchIterator->key); 159 160 result = BTSearchRecord(fcb, searchIterator, &btRecord, &tempSize, searchIterator); 161 if (result == noErr) { 162 *newHint = searchIterator->hint.nodeNum; 163 BlockMoveData(&searchIterator->key, keyPtr, CalcKeySize(btcb, &searchIterator->key)); 164 } 165 166 FREE (searchIterator, M_TEMP); 167 return (result == btNotFound ? cmNotFound : result); 168} 169 170 171 172/* 173 * Routine: BuildCatalogKey 174 * 175 * Function: Constructs a catalog key record (ckr) given the parent 176 * folder ID and CName. Works for both classic and extended 177 * HFS volumes. 178 * 179 */ 180 181void 182BuildCatalogKey(HFSCatalogNodeID parentID, const CatalogName *cName, Boolean isHFSPlus, CatalogKey *key) 183{ 184 if ( isHFSPlus ) 185 { 186 key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; // initial key length (4 + 2) 187 key->hfsPlus.parentID = parentID; // set parent ID 188 key->hfsPlus.nodeName.length = 0; // null CName length 189 if ( cName != NULL ) 190 { 191 CopyCatalogName(cName, (CatalogName *) &key->hfsPlus.nodeName, isHFSPlus); 192 key->hfsPlus.keyLength += sizeof(UniChar) * cName->ustr.length; // add CName size to key length 193 } 194 } 195 else 196 { 197 key->hfs.keyLength = kHFSCatalogKeyMinimumLength; // initial key length (1 + 4 + 1) 198 key->hfs.reserved = 0; // clear unused byte 199 key->hfs.parentID = parentID; // set parent ID 200 key->hfs.nodeName[0] = 0; // null CName length 201 if ( cName != NULL ) 202 { 203 UpdateCatalogName(cName->pstr, key->hfs.nodeName); 204 key->hfs.keyLength += key->hfs.nodeName[0]; // add CName size to key length 205 } 206 } 207} 208 209OSErr 210BuildCatalogKeyUTF8(ExtendedVCB *volume, HFSCatalogNodeID parentID, const unsigned char *name, u_int32_t nameLength, 211 CatalogKey *key, u_int32_t *textEncoding) 212{ 213 OSErr err = 0; 214 215 if ( name == NULL) 216 nameLength = 0; 217 else if (nameLength == kUndefinedStrLen) 218 nameLength = strlen((const char *)name); 219 220 if ( volume->vcbSigWord == kHFSPlusSigWord ) { 221 size_t unicodeBytes = 0; 222 223 key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; // initial key length (4 + 2) 224 key->hfsPlus.parentID = parentID; // set parent ID 225 key->hfsPlus.nodeName.length = 0; // null CName length 226 if ( nameLength > 0 ) { 227 err = utf8_decodestr(name, nameLength, key->hfsPlus.nodeName.unicode, 228 &unicodeBytes, sizeof(key->hfsPlus.nodeName.unicode), ':', UTF_DECOMPOSED); 229 key->hfsPlus.nodeName.length = unicodeBytes / sizeof(UniChar); 230 key->hfsPlus.keyLength += unicodeBytes; 231 } 232 233 if (textEncoding && (*textEncoding != kTextEncodingMacUnicode)) 234 *textEncoding = hfs_pickencoding(key->hfsPlus.nodeName.unicode, 235 key->hfsPlus.nodeName.length); 236 } 237 else { 238 key->hfs.keyLength = kHFSCatalogKeyMinimumLength; // initial key length (1 + 4 + 1) 239 key->hfs.reserved = 0; // clear unused byte 240 key->hfs.parentID = parentID; // set parent ID 241 key->hfs.nodeName[0] = 0; // null CName length 242 if ( nameLength > 0 ) { 243 err = utf8_to_hfs(volume, nameLength, name, &key->hfs.nodeName[0]); 244 /* 245 * Retry with MacRoman in case that's how it was exported. 246 * When textEncoding != NULL we know that this is a create 247 * or rename call and can skip the retry (ugly but it works). 248 */ 249 if (err && (textEncoding == NULL)) 250 err = utf8_to_mac_roman(nameLength, name, &key->hfs.nodeName[0]); 251 key->hfs.keyLength += key->hfs.nodeName[0]; // add CName size to key length 252 } 253 if (textEncoding) 254 *textEncoding = 0; 255 } 256 257 if (err) { 258 if (err == ENAMETOOLONG) 259 err = bdNamErr; /* name is too long */ 260 else 261 err = paramErr; /* name has invalid characters */ 262 } 263 264 return err; 265} 266 267 268//******************************************************************************* 269// Routine: FlushCatalog 270// 271// Function: Flushes the catalog for a specified volume. 272// 273//******************************************************************************* 274 275OSErr 276FlushCatalog(ExtendedVCB *volume) 277{ 278 FCB * fcb; 279 OSErr result; 280 281 fcb = GetFileControlBlock(volume->catalogRefNum); 282 result = BTFlushPath(fcb); 283 284 if (result == noErr) 285 { 286 //--- check if catalog's fcb is dirty... 287 288 if ( 0 /*fcb->fcbFlags & fcbModifiedMask*/ ) 289 { 290 HFS_MOUNT_LOCK(volume, TRUE); 291 MarkVCBDirty(volume); // Mark the VCB dirty 292 volume->vcbLsMod = GetTimeUTC(); // update last modified date 293 HFS_MOUNT_UNLOCK(volume, TRUE); 294 295 // result = FlushVolumeControlBlock(volume); 296 } 297 } 298 299 return result; 300} 301 302 303//������������������������������������������������������������������������������� 304// Routine: UpdateCatalogName 305// 306// Function: Updates a CName. 307// 308//������������������������������������������������������������������������������� 309 310void 311UpdateCatalogName(ConstStr31Param srcName, Str31 destName) 312{ 313 Size length = srcName[0]; 314 315 if (length > CMMaxCName) 316 length = CMMaxCName; // truncate to max 317 318 destName[0] = length; // set length byte 319 320 BlockMoveData(&srcName[1], &destName[1], length); 321} 322 323//_______________________________________________________________________ 324 325void 326CopyCatalogName(const CatalogName *srcName, CatalogName *dstName, Boolean isHFSPLus) 327{ 328 u_int32_t length; 329 330 if ( srcName == NULL ) 331 { 332 if ( dstName != NULL ) 333 dstName->ustr.length = 0; // set length byte to zero (works for both unicode and pascal) 334 return; 335 } 336 337 if (isHFSPLus) 338 length = sizeof(UniChar) * (srcName->ustr.length + 1); 339 else 340 length = sizeof(u_int8_t) + srcName->pstr[0]; 341 342 if ( length > 1 ) 343 BlockMoveData(srcName, dstName, length); 344 else 345 dstName->ustr.length = 0; // set length byte to zero (works for both unicode and pascal) 346} 347 348