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