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
100#if CONFIG_HFS_STD
101		case kHFSFileThreadRecord:
102		case kHFSFolderThreadRecord:
103			threadParentID = dataPtr->hfsThread.parentID;
104			nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName;
105			break;
106#endif
107
108		case kHFSPlusFileThreadRecord:
109		case kHFSPlusFolderThreadRecord:
110			threadParentID = dataPtr->hfsPlusThread.parentID;
111			nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName;
112			break;
113
114		default:
115			threadParentID = 0;
116			break;
117	}
118
119	if ( threadParentID )		// found a thread
120		result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint);
121
122	FREE (searchIterator, M_TEMP);
123	return result;
124}
125
126
127
128//*******************************************************************************
129//	Routine:	LocateCatalogRecord
130//
131// Function: 	Locates the catalog record associated with folderID and name
132//
133//*******************************************************************************
134
135OSErr
136LocateCatalogRecord(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name,
137					__unused u_int32_t hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, u_int32_t *newHint)
138{
139	OSErr result;
140	uint16_t tempSize;
141	FSBufferDescriptor btRecord;
142	struct BTreeIterator *searchIterator = NULL;
143	FCB *fcb;
144	BTreeControlBlock *btcb;
145
146	MALLOC (searchIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
147	if (searchIterator == NULL) {
148		return memFullErr;  // translates to ENOMEM
149	}
150
151	bzero(searchIterator, sizeof(*searchIterator));
152
153
154	fcb = GetFileControlBlock(volume->catalogRefNum);
155	btcb = (BTreeControlBlock *)fcb->fcbBTCBPtr;
156
157	btRecord.bufferAddress = dataPtr;
158	btRecord.itemCount = 1;
159	btRecord.itemSize = sizeof(CatalogRecord);
160
161	BuildCatalogKey(folderID, name, (volume->vcbSigWord == kHFSPlusSigWord), (CatalogKey *)&searchIterator->key);
162
163	result = BTSearchRecord(fcb, searchIterator, &btRecord, &tempSize, searchIterator);
164	if (result == noErr) {
165		*newHint = searchIterator->hint.nodeNum;
166		BlockMoveData(&searchIterator->key, keyPtr, CalcKeySize(btcb, &searchIterator->key));
167	}
168
169	FREE (searchIterator, M_TEMP);
170	return (result == btNotFound ? cmNotFound : result);
171}
172
173
174
175/*
176 *	Routine:	BuildCatalogKey
177 *
178 *	Function: 	Constructs a catalog key record (ckr) given the parent
179 *				folder ID and CName.  Works for both classic and extended
180 *				HFS volumes.
181 *
182 */
183
184void
185BuildCatalogKey(HFSCatalogNodeID parentID, const CatalogName *cName, Boolean isHFSPlus, CatalogKey *key)
186{
187	if ( isHFSPlus )
188	{
189		key->hfsPlus.keyLength			= kHFSPlusCatalogKeyMinimumLength;	// initial key length (4 + 2)
190		key->hfsPlus.parentID			= parentID;		// set parent ID
191		key->hfsPlus.nodeName.length	= 0;			// null CName length
192		if ( cName != NULL )
193		{
194			CopyCatalogName(cName, (CatalogName *) &key->hfsPlus.nodeName, isHFSPlus);
195			key->hfsPlus.keyLength += sizeof(UniChar) * cName->ustr.length;	// add CName size to key length
196		}
197	}
198#if CONFIG_HFS_STD
199	else
200	{
201		key->hfs.keyLength		= kHFSCatalogKeyMinimumLength;	// initial key length (1 + 4 + 1)
202		key->hfs.reserved		= 0;				// clear unused byte
203		key->hfs.parentID		= parentID;			// set parent ID
204		key->hfs.nodeName[0]	= 0;				// null CName length
205		if ( cName != NULL )
206		{
207			UpdateCatalogName(cName->pstr, key->hfs.nodeName);
208			key->hfs.keyLength += key->hfs.nodeName[0];		// add CName size to key length
209		}
210	}
211#endif
212
213}
214
215OSErr
216BuildCatalogKeyUTF8(ExtendedVCB *volume, HFSCatalogNodeID parentID, const unsigned char *name, u_int32_t nameLength,
217		    CatalogKey *key, u_int32_t *textEncoding)
218{
219	OSErr err = 0;
220
221    if ( name == NULL)
222        nameLength = 0;
223    else if (nameLength == kUndefinedStrLen)
224        nameLength = strlen((const char *)name);
225
226	if ( volume->vcbSigWord == kHFSPlusSigWord ) {
227		size_t unicodeBytes = 0;
228
229		key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;	// initial key length (4 + 2)
230		key->hfsPlus.parentID = parentID;			// set parent ID
231		key->hfsPlus.nodeName.length = 0;			// null CName length
232		if ( nameLength > 0 ) {
233			err = utf8_decodestr(name, nameLength, key->hfsPlus.nodeName.unicode,
234				&unicodeBytes, sizeof(key->hfsPlus.nodeName.unicode), ':', UTF_DECOMPOSED);
235			key->hfsPlus.nodeName.length = unicodeBytes / sizeof(UniChar);
236			key->hfsPlus.keyLength += unicodeBytes;
237		}
238
239		if (textEncoding && (*textEncoding != kTextEncodingMacUnicode))
240			*textEncoding = hfs_pickencoding(key->hfsPlus.nodeName.unicode,
241				key->hfsPlus.nodeName.length);
242	}
243#if CONFIG_HFS_STD
244	else {
245		key->hfs.keyLength		= kHFSCatalogKeyMinimumLength;	// initial key length (1 + 4 + 1)
246		key->hfs.reserved		= 0;				// clear unused byte
247		key->hfs.parentID		= parentID;			// set parent ID
248		key->hfs.nodeName[0]	= 0;				// null CName length
249		if ( nameLength > 0 ) {
250			err = utf8_to_hfs(volume, nameLength, name, &key->hfs.nodeName[0]);
251			/*
252			 * Retry with MacRoman in case that's how it was exported.
253			 * When textEncoding != NULL we know that this is a create
254			 * or rename call and can skip the retry (ugly but it works).
255			 */
256			if (err && (textEncoding == NULL))
257				err = utf8_to_mac_roman(nameLength, name, &key->hfs.nodeName[0]);
258			key->hfs.keyLength += key->hfs.nodeName[0];		// add CName size to key length
259		}
260		if (textEncoding)
261			*textEncoding = 0;
262	}
263#endif
264
265	if (err) {
266		if (err == ENAMETOOLONG)
267			err = bdNamErr;	/* name is too long */
268		else
269			err = paramErr;	/* name has invalid characters */
270	}
271
272	return err;
273}
274
275
276//*******************************************************************************
277//	Routine:	FlushCatalog
278//
279// Function: 	Flushes the catalog for a specified volume.
280//
281//*******************************************************************************
282
283OSErr
284FlushCatalog(ExtendedVCB *volume)
285{
286	FCB *	fcb;
287	OSErr	result;
288	struct hfsmount *hfsmp = VCBTOHFS (volume);
289
290	fcb = GetFileControlBlock(volume->catalogRefNum);
291	result = BTFlushPath(fcb);
292
293	if (result == noErr)
294	{
295		//--- check if catalog's fcb is dirty...
296
297		if ( 0 /*fcb->fcbFlags & fcbModifiedMask*/ )
298		{
299			hfs_lock_mount (hfsmp);
300			MarkVCBDirty(volume);	// Mark the VCB dirty
301			volume->vcbLsMod = GetTimeUTC();	// update last modified date
302			hfs_unlock_mount (hfsmp);
303
304		//	result = FlushVolumeControlBlock(volume);
305		}
306	}
307
308	return result;
309}
310
311
312//�������������������������������������������������������������������������������
313//	Routine:	UpdateCatalogName
314//
315//	Function: 	Updates a CName.
316//
317//�������������������������������������������������������������������������������
318
319void
320UpdateCatalogName(ConstStr31Param srcName, Str31 destName)
321{
322	Size length = srcName[0];
323
324	if (length > CMMaxCName)
325		length = CMMaxCName;				// truncate to max
326
327	destName[0] = length;					// set length byte
328
329	BlockMoveData(&srcName[1], &destName[1], length);
330}
331
332//_______________________________________________________________________
333
334void
335CopyCatalogName(const CatalogName *srcName, CatalogName *dstName, Boolean isHFSPlus)
336{
337	u_int32_t	length = 0;
338
339	if ( srcName == NULL )
340	{
341		if ( dstName != NULL )
342			dstName->ustr.length = 0;	// set length byte to zero (works for both unicode and pascal)
343		return;
344	}
345
346	if (isHFSPlus) {
347		length = sizeof(UniChar) * (srcName->ustr.length + 1);
348	}
349#if CONFIG_HFS_STD
350	else {
351		length = sizeof(u_int8_t) + srcName->pstr[0];
352	}
353#endif
354
355	if ( length > 1 )
356		BlockMoveData(srcName, dstName, length);
357	else
358		dstName->ustr.length = 0;	// set length byte to zero (works for both unicode and pascal)
359}
360
361