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 32#include "../headers/FileMgrInternal.h" 33#include "../headers/BTreesInternal.h" 34#include "../headers/CatalogPrivate.h" 35#include "../headers/HFSUnicodeWrappers.h" 36#include <string.h> 37 38 39//******************************************************************************* 40// Routine: LocateCatalogNode 41// 42// Function: Locates the catalog record for an existing folder or file 43// CNode and returns pointers to the key and data records. 44// 45//******************************************************************************* 46 47OSErr 48LocateCatalogNode(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name, 49 u_int32_t hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, u_int32_t *newHint) 50{ 51 OSErr result; 52 CatalogName *nodeName = NULL; /* To ward off uninitialized use warnings from compiler */ 53 HFSCatalogNodeID threadParentID; 54 55 56 result = LocateCatalogRecord(volume, folderID, name, hint, keyPtr, dataPtr, newHint); 57 ReturnIfError(result); 58 59 // if we got a thread record, then go look up real record 60 switch ( dataPtr->recordType ) 61 { 62 case kHFSFileThreadRecord: 63 case kHFSFolderThreadRecord: 64 threadParentID = dataPtr->hfsThread.parentID; 65 nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName; 66 break; 67 68 case kHFSPlusFileThreadRecord: 69 case kHFSPlusFolderThreadRecord: 70 threadParentID = dataPtr->hfsPlusThread.parentID; 71 nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName; 72 break; 73 74 default: 75 threadParentID = 0; 76 break; 77 } 78 79 if ( threadParentID ) // found a thread 80 result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint); 81 82 return result; 83} 84 85// 86// Routine: LocateCatalogNodeByKey 87// 88// Function: Locates the catalog record for an existing folder or file 89// CNode and returns the key and data records. 90// 91 92OSErr 93LocateCatalogNodeByKey(const ExtendedVCB *volume, u_int32_t hint, CatalogKey *keyPtr, 94 CatalogRecord *dataPtr, u_int32_t *newHint) 95{ 96 OSErr result; 97 CatalogName *nodeName = NULL; 98 HFSCatalogNodeID threadParentID; 99 u_int16_t tempSize; 100 FSBufferDescriptor btRecord; 101 BTreeIterator searchIterator; 102 FCB *fcb; 103 104 bzero(&searchIterator, sizeof(searchIterator)); 105 106 fcb = GetFileControlBlock(volume->catalogRefNum); 107 108 btRecord.bufferAddress = dataPtr; 109 btRecord.itemCount = 1; 110 btRecord.itemSize = sizeof(CatalogRecord); 111 112 searchIterator.hint.nodeNum = hint; 113 114 bcopy(keyPtr, &searchIterator.key, sizeof(CatalogKey)); 115 116 result = BTSearchRecord( fcb, &searchIterator, &btRecord, &tempSize, &searchIterator ); 117 118 if (result == noErr) 119 { 120 *newHint = searchIterator.hint.nodeNum; 121 122 BlockMoveData(&searchIterator.key, keyPtr, sizeof(CatalogKey)); 123 } 124 125 if (result == btNotFound) 126 result = cmNotFound; 127 ReturnIfError(result); 128 129 // if we got a thread record, then go look up real record 130 switch ( dataPtr->recordType ) 131 { 132 case kHFSFileThreadRecord: 133 case kHFSFolderThreadRecord: 134 threadParentID = dataPtr->hfsThread.parentID; 135 nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName; 136 break; 137 138 case kHFSPlusFileThreadRecord: 139 case kHFSPlusFolderThreadRecord: 140 threadParentID = dataPtr->hfsPlusThread.parentID; 141 nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName; 142 break; 143 144 default: 145 threadParentID = 0; 146 break; 147 } 148 149 if ( threadParentID ) // found a thread 150 result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint); 151 152 return result; 153} 154 155 156 157//******************************************************************************* 158// Routine: LocateCatalogRecord 159// 160// Function: Locates the catalog record associated with folderID and name 161// 162//******************************************************************************* 163 164OSErr 165LocateCatalogRecord(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name, 166 u_int32_t hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, u_int32_t *newHint) 167{ 168 OSErr result; 169 CatalogKey tempKey; // 518 bytes 170 u_int16_t tempSize; 171 172 BuildCatalogKey(folderID, name, (volume->vcbSigWord == kHFSPlusSigWord), &tempKey); 173 174 if ( name == NULL ) 175 hint = kNoHint; // no CName given so clear the hint 176 177 result = SearchBTreeRecord(volume->catalogRefNum, &tempKey, hint, keyPtr, dataPtr, &tempSize, newHint); 178 179 return (result == btNotFound ? cmNotFound : result); 180} 181 182 183 184/* 185 * Routine: BuildCatalogKey 186 * 187 * Function: Constructs a catalog key record (ckr) given the parent 188 * folder ID and CName. Works for both classic and extended 189 * HFS volumes. 190 * 191 */ 192 193void 194BuildCatalogKey(HFSCatalogNodeID parentID, const CatalogName *cName, Boolean isHFSPlus, CatalogKey *key) 195{ 196 if ( isHFSPlus ) 197 { 198 key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; // initial key length (4 + 2) 199 key->hfsPlus.parentID = parentID; // set parent ID 200 key->hfsPlus.nodeName.length = 0; // null CName length 201 if ( cName != NULL ) 202 { 203 CopyCatalogName(cName, (CatalogName *) &key->hfsPlus.nodeName, isHFSPlus); 204 key->hfsPlus.keyLength += sizeof(UniChar) * cName->ustr.length; // add CName size to key length 205 } 206 } 207 else 208 { 209 key->hfs.keyLength = kHFSCatalogKeyMinimumLength; // initial key length (1 + 4 + 1) 210 key->hfs.reserved = 0; // clear unused byte 211 key->hfs.parentID = parentID; // set parent ID 212 key->hfs.nodeName[0] = 0; // null CName length 213 if ( cName != NULL ) 214 { 215 UpdateCatalogName(cName->pstr, key->hfs.nodeName); 216 key->hfs.keyLength += key->hfs.nodeName[0]; // add CName size to key length 217 } 218 } 219} 220 221OSErr 222BuildCatalogKeyUTF8(ExtendedVCB *volume, HFSCatalogNodeID parentID, const unsigned char *name, u_int32_t nameLength, 223 CatalogKey *key, u_int32_t *textEncoding) 224{ 225 OSErr err = 0; 226 227 if ( name == NULL) 228 nameLength = 0; 229 else if (nameLength == kUndefinedStrLen) 230 nameLength = strlen((const char *)name); 231 232 if ( volume->vcbSigWord == kHFSPlusSigWord ) { 233 size_t unicodeBytes = 0; 234 235 key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; // initial key length (4 + 2) 236 key->hfsPlus.parentID = parentID; // set parent ID 237 key->hfsPlus.nodeName.length = 0; // null CName length 238 if ( nameLength > 0 ) { 239 err = utf8_decodestr(name, nameLength, key->hfsPlus.nodeName.unicode, 240 &unicodeBytes, sizeof(key->hfsPlus.nodeName.unicode), ':', UTF_DECOMPOSED); 241 key->hfsPlus.nodeName.length = unicodeBytes / sizeof(UniChar); 242 key->hfsPlus.keyLength += unicodeBytes; 243 } 244 245 if (textEncoding && (*textEncoding != kTextEncodingMacUnicode)) 246 *textEncoding = hfs_pickencoding(key->hfsPlus.nodeName.unicode, 247 key->hfsPlus.nodeName.length); 248 } 249 else { 250 key->hfs.keyLength = kHFSCatalogKeyMinimumLength; // initial key length (1 + 4 + 1) 251 key->hfs.reserved = 0; // clear unused byte 252 key->hfs.parentID = parentID; // set parent ID 253 key->hfs.nodeName[0] = 0; // null CName length 254 if ( nameLength > 0 ) { 255 err = utf8_to_hfs(volume, nameLength, name, &key->hfs.nodeName[0]); 256 /* 257 * Retry with MacRoman in case that's how it was exported. 258 * When textEncoding != NULL we know that this is a create 259 * or rename call and can skip the retry (ugly but it works). 260 */ 261 if (err && (textEncoding == NULL)) 262 err = utf8_to_mac_roman(nameLength, name, &key->hfs.nodeName[0]); 263 key->hfs.keyLength += key->hfs.nodeName[0]; // add CName size to key length 264 } 265 if (textEncoding) 266 *textEncoding = 0; 267 } 268 269 if (err) { 270 if (err == ENAMETOOLONG) 271 err = bdNamErr; /* name is too long */ 272 else 273 err = paramErr; /* name has invalid characters */ 274 } 275 276 return err; 277} 278 279 280//******************************************************************************* 281// Routine: FlushCatalog 282// 283// Function: Flushes the catalog for a specified volume. 284// 285//******************************************************************************* 286 287OSErr 288FlushCatalog(ExtendedVCB *volume) 289{ 290 FCB * fcb; 291 OSErr result; 292 293 fcb = GetFileControlBlock(volume->catalogRefNum); 294 result = BTFlushPath(fcb); 295 296 if (result == noErr) 297 { 298 //--- check if catalog's fcb is dirty... 299 300 if ( 0 /*fcb->fcbFlags & fcbModifiedMask*/ ) 301 { 302 HFS_MOUNT_LOCK(volume, TRUE); 303 MarkVCBDirty(volume); // Mark the VCB dirty 304 volume->vcbLsMod = GetTimeUTC(); // update last modified date 305 HFS_MOUNT_UNLOCK(volume, TRUE); 306 307 // result = FlushVolumeControlBlock(volume); 308 } 309 } 310 311 return result; 312} 313 314 315//������������������������������������������������������������������������������� 316// Routine: UpdateCatalogName 317// 318// Function: Updates a CName. 319// 320//������������������������������������������������������������������������������� 321 322void 323UpdateCatalogName(ConstStr31Param srcName, Str31 destName) 324{ 325 Size length = srcName[0]; 326 327 if (length > CMMaxCName) 328 length = CMMaxCName; // truncate to max 329 330 destName[0] = length; // set length byte 331 332 BlockMoveData(&srcName[1], &destName[1], length); 333} 334 335//_______________________________________________________________________ 336 337void 338CopyCatalogName(const CatalogName *srcName, CatalogName *dstName, Boolean isHFSPLus) 339{ 340 u_int32_t length; 341 342 if ( srcName == NULL ) 343 { 344 if ( dstName != NULL ) 345 dstName->ustr.length = 0; // set length byte to zero (works for both unicode and pascal) 346 return; 347 } 348 349 if (isHFSPLus) 350 length = sizeof(UniChar) * (srcName->ustr.length + 1); 351 else 352 length = sizeof(u_int8_t) + srcName->pstr[0]; 353 354 if ( length > 1 ) 355 BlockMoveData(srcName, dstName, length); 356 else 357 dstName->ustr.length = 0; // set length byte to zero (works for both unicode and pascal) 358} 359 360