1/*
2 * Copyright (c) 2000, 2002, 2005-2008 Apple 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
29#include "../headers/BTreesPrivate.h"
30#include <sys/kernel.h>
31#include <sys/malloc.h>
32#include <libkern/libkern.h>
33
34
35// local routines
36static OSErr	CheckBTreeKey(const BTreeKey *key, const BTreeControlBlock *btcb);
37static Boolean	ValidHFSRecord(const void *record, const BTreeControlBlock *btcb, u_int16_t recordSize);
38
39
40OSErr ReplaceBTreeRecord(FileReference refNum, const void* key, u_int32_t hint, void *newData, u_int16_t dataSize, u_int32_t *newHint)
41{
42	FSBufferDescriptor	btRecord;
43	struct BTreeIterator *iterator = NULL;
44	FCB					*fcb;
45	BTreeControlBlock	*btcb;
46	OSStatus			result;
47
48	MALLOC (iterator, struct BTreeIterator *, sizeof (struct BTreeIterator), M_TEMP, M_WAITOK);
49	if (iterator == NULL) {
50		return memFullErr;  //translates to ENOMEM
51	}
52	bzero (iterator, sizeof (*iterator));
53
54	fcb = GetFileControlBlock(refNum);
55	btcb = (BTreeControlBlock*) fcb->fcbBTCBPtr;
56
57	btRecord.bufferAddress = newData;
58	btRecord.itemSize = dataSize;
59	btRecord.itemCount = 1;
60
61	iterator->hint.nodeNum = hint;
62
63	result = CheckBTreeKey((const BTreeKey *) key, btcb);
64	if (result) {
65		goto ErrorExit;
66	}
67
68	BlockMoveData(key, &iterator->key, CalcKeySize(btcb, (const BTreeKey *) key));		//�� should we range check against maxkeylen?
69
70	if ( DEBUG_BUILD && !ValidHFSRecord(newData, btcb, dataSize) )
71		DebugStr("ReplaceBTreeRecord: bad record?");
72
73	result = BTReplaceRecord( fcb, iterator, &btRecord, dataSize );
74
75	*newHint = iterator->hint.nodeNum;
76
77ErrorExit:
78
79	FREE (iterator, M_TEMP);
80	return result;
81}
82
83
84
85static OSErr CheckBTreeKey(const BTreeKey *key, const BTreeControlBlock *btcb)
86{
87	u_int16_t	keyLen;
88
89	if ( btcb->attributes & kBTBigKeysMask )
90		keyLen = key->length16;
91	else
92		keyLen = key->length8;
93
94	if ( (keyLen < 6) || (keyLen > btcb->maxKeyLength) )
95	{
96		if ( DEBUG_BUILD )
97			DebugStr("CheckBTreeKey: bad key length!");
98		return fsBTInvalidKeyLengthErr;
99	}
100
101	return noErr;
102}
103
104
105static Boolean ValidHFSRecord(const void *record, const BTreeControlBlock *btcb, u_int16_t recordSize)
106{
107	u_int32_t			cNodeID;
108
109	if ( btcb->maxKeyLength == kHFSExtentKeyMaximumLength )
110	{
111		return ( recordSize == sizeof(HFSExtentRecord) );
112	}
113	else if (btcb->maxKeyLength == kHFSPlusExtentKeyMaximumLength )
114	{
115		return ( recordSize == sizeof(HFSPlusExtentRecord) );
116	}
117	else // Catalog record
118	{
119		const CatalogRecord *catalogRecord = (const CatalogRecord*) record;
120
121		switch(catalogRecord->recordType)
122		{
123			case kHFSFolderRecord:
124			{
125				if ( recordSize != sizeof(HFSCatalogFolder) )
126					return false;
127				if ( catalogRecord->hfsFolder.flags != 0 )
128					return false;
129				if ( catalogRecord->hfsFolder.valence > 0x7FFF )
130					return false;
131
132				cNodeID = catalogRecord->hfsFolder.folderID;
133
134				if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
135					return false;
136			}
137			break;
138
139			case kHFSPlusFolderRecord:
140			{
141				if ( recordSize != sizeof(HFSPlusCatalogFolder) )
142					return false;
143				if ( catalogRecord->hfsPlusFolder.flags != 0 )
144					return false;
145				if ( catalogRecord->hfsPlusFolder.valence > 0x7FFF )
146					return false;
147
148				cNodeID = catalogRecord->hfsPlusFolder.folderID;
149
150				if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
151					return false;
152			}
153			break;
154
155			case kHFSFileRecord:
156			{
157//				u_int16_t					i;
158				HFSExtentDescriptor	*dataExtent;
159				HFSExtentDescriptor	*rsrcExtent;
160
161				if ( recordSize != sizeof(HFSCatalogFile) )
162					return false;
163				if ( (catalogRecord->hfsFile.flags & ~(0x83)) != 0 )
164					return false;
165
166				cNodeID = catalogRecord->hfsFile.fileID;
167
168				if ( cNodeID < 16 )
169					return false;
170
171				// make sure 0 � LEOF � PEOF for both forks
172
173				if ( catalogRecord->hfsFile.dataLogicalSize < 0 )
174					return false;
175				if ( catalogRecord->hfsFile.dataPhysicalSize < catalogRecord->hfsFile.dataLogicalSize )
176					return false;
177				if ( catalogRecord->hfsFile.rsrcLogicalSize < 0 )
178					return false;
179				if ( catalogRecord->hfsFile.rsrcPhysicalSize < catalogRecord->hfsFile.rsrcLogicalSize )
180					return false;
181
182				dataExtent = (HFSExtentDescriptor*) &catalogRecord->hfsFile.dataExtents;
183				rsrcExtent = (HFSExtentDescriptor*) &catalogRecord->hfsFile.rsrcExtents;
184
185#if 0
186				for (i = 0; i < kHFSExtentDensity; ++i)
187				{
188					if ( (dataExtent[i].blockCount > 0) && (dataExtent[i].startBlock == 0) )
189						return false;
190					if ( (rsrcExtent[i].blockCount > 0) && (rsrcExtent[i].startBlock == 0) )
191						return false;
192				}
193#endif
194			}
195			break;
196
197			case kHFSPlusFileRecord:
198			{
199//				u_int16_t					i;
200				HFSPlusExtentDescriptor	*dataExtent;
201				HFSPlusExtentDescriptor	*rsrcExtent;
202
203				if ( recordSize != sizeof(HFSPlusCatalogFile) )
204					return false;
205				if ( (catalogRecord->hfsPlusFile.flags & ~(0x83)) != 0 )
206					return false;
207
208				cNodeID = catalogRecord->hfsPlusFile.fileID;
209
210				if ( cNodeID < 16 )
211					return false;
212
213				// make sure 0 � LEOF � PEOF for both forks
214
215				dataExtent = (HFSPlusExtentDescriptor*) &catalogRecord->hfsPlusFile.dataFork.extents;
216				rsrcExtent = (HFSPlusExtentDescriptor*) &catalogRecord->hfsPlusFile.resourceFork.extents;
217
218#if 0
219				for (i = 0; i < kHFSPlusExtentDensity; ++i)
220				{
221					if ( (dataExtent[i].blockCount > 0) && (dataExtent[i].startBlock == 0) )
222						return false;
223					if ( (rsrcExtent[i].blockCount > 0) && (rsrcExtent[i].startBlock == 0) )
224						return false;
225				}
226#endif
227			}
228			break;
229
230			case kHFSFolderThreadRecord:
231			case kHFSFileThreadRecord:
232			{
233				if ( recordSize != sizeof(HFSCatalogThread) )
234					return false;
235
236				cNodeID = catalogRecord->hfsThread.parentID;
237				if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
238					return false;
239
240				if ( (catalogRecord->hfsThread.nodeName[0] == 0) ||
241					 (catalogRecord->hfsThread.nodeName[0] > 31) )
242					return false;
243			}
244			break;
245
246			case kHFSPlusFolderThreadRecord:
247			case kHFSPlusFileThreadRecord:
248			{
249				if ( recordSize > sizeof(HFSPlusCatalogThread) || recordSize < (sizeof(HFSPlusCatalogThread) - sizeof(HFSUniStr255)))
250					return false;
251
252				cNodeID = catalogRecord->hfsPlusThread.parentID;
253				if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
254					return false;
255
256				if ( (catalogRecord->hfsPlusThread.nodeName.length == 0) ||
257					 (catalogRecord->hfsPlusThread.nodeName.length > 255) )
258					return false;
259			}
260			break;
261
262			default:
263				return false;
264		}
265	}
266
267	return true;	// record appears to be OK
268}
269