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