1/*
2 * Copyright (c) 2000, 2002, 2005-2013 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 == kHFSPlusExtentKeyMaximumLength )
110	{
111		return ( recordSize == sizeof(HFSPlusExtentRecord) );
112	}
113#if CONFIG_HFS_STD
114	else if ( btcb->maxKeyLength == kHFSExtentKeyMaximumLength )
115	{
116		return ( recordSize == sizeof(HFSExtentRecord) );
117	}
118#endif
119
120	else // Catalog record
121	{
122		const CatalogRecord *catalogRecord = (const CatalogRecord*) record;
123
124		switch(catalogRecord->recordType)
125		{
126
127#if CONFIG_HFS_STD
128			/*
129			 * HFS standard File/folder records and File/Folder Thread records
130			 * are only valid on configs that support HFS standard.
131			 */
132			case kHFSFolderRecord:
133			{
134				if ( recordSize != sizeof(HFSCatalogFolder) )
135					return false;
136				if ( catalogRecord->hfsFolder.flags != 0 )
137					return false;
138				if ( catalogRecord->hfsFolder.valence > 0x7FFF )
139					return false;
140
141				cNodeID = catalogRecord->hfsFolder.folderID;
142
143				if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
144					return false;
145			}
146			break;
147
148			case kHFSFileRecord:
149			{
150				HFSExtentDescriptor	*dataExtent;
151				HFSExtentDescriptor	*rsrcExtent;
152
153				if ( recordSize != sizeof(HFSCatalogFile) )
154					return false;
155				if ( (catalogRecord->hfsFile.flags & ~(0x83)) != 0 )
156					return false;
157
158				cNodeID = catalogRecord->hfsFile.fileID;
159
160				if ( cNodeID < 16 )
161					return false;
162
163				// make sure 0 � LEOF � PEOF for both forks
164
165				if ( catalogRecord->hfsFile.dataLogicalSize < 0 )
166					return false;
167				if ( catalogRecord->hfsFile.dataPhysicalSize < catalogRecord->hfsFile.dataLogicalSize )
168					return false;
169				if ( catalogRecord->hfsFile.rsrcLogicalSize < 0 )
170					return false;
171				if ( catalogRecord->hfsFile.rsrcPhysicalSize < catalogRecord->hfsFile.rsrcLogicalSize )
172					return false;
173
174				dataExtent = (HFSExtentDescriptor*) &catalogRecord->hfsFile.dataExtents;
175				rsrcExtent = (HFSExtentDescriptor*) &catalogRecord->hfsFile.rsrcExtents;
176
177#if 0
178				for (i = 0; i < kHFSExtentDensity; ++i)
179				{
180					if ( (dataExtent[i].blockCount > 0) && (dataExtent[i].startBlock == 0) )
181						return false;
182					if ( (rsrcExtent[i].blockCount > 0) && (rsrcExtent[i].startBlock == 0) )
183						return false;
184				}
185#endif
186			}
187			break;
188
189			case kHFSFileThreadRecord:
190			case kHFSFolderThreadRecord:
191			{
192				if ( recordSize != sizeof(HFSCatalogThread) )
193					return false;
194
195				cNodeID = catalogRecord->hfsThread.parentID;
196				if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
197					return false;
198
199				if ( (catalogRecord->hfsThread.nodeName[0] == 0) ||
200					 (catalogRecord->hfsThread.nodeName[0] > 31) )
201					return false;
202			}
203			break;
204#endif
205
206			case kHFSPlusFolderRecord:
207			{
208				if ( recordSize != sizeof(HFSPlusCatalogFolder) )
209					return false;
210				if ( catalogRecord->hfsPlusFolder.flags != 0 )
211					return false;
212				if ( catalogRecord->hfsPlusFolder.valence > 0x7FFF )
213					return false;
214
215				cNodeID = catalogRecord->hfsPlusFolder.folderID;
216
217				if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
218					return false;
219			}
220			break;
221
222			case kHFSPlusFileRecord:
223			{
224//				u_int16_t					i;
225				HFSPlusExtentDescriptor	*dataExtent;
226				HFSPlusExtentDescriptor	*rsrcExtent;
227
228				if ( recordSize != sizeof(HFSPlusCatalogFile) )
229					return false;
230				if ( (catalogRecord->hfsPlusFile.flags & ~(0x83)) != 0 )
231					return false;
232
233				cNodeID = catalogRecord->hfsPlusFile.fileID;
234
235				if ( cNodeID < 16 )
236					return false;
237
238				// make sure 0 � LEOF � PEOF for both forks
239
240				dataExtent = (HFSPlusExtentDescriptor*) &catalogRecord->hfsPlusFile.dataFork.extents;
241				rsrcExtent = (HFSPlusExtentDescriptor*) &catalogRecord->hfsPlusFile.resourceFork.extents;
242
243#if 0
244				for (i = 0; i < kHFSPlusExtentDensity; ++i)
245				{
246					if ( (dataExtent[i].blockCount > 0) && (dataExtent[i].startBlock == 0) )
247						return false;
248					if ( (rsrcExtent[i].blockCount > 0) && (rsrcExtent[i].startBlock == 0) )
249						return false;
250				}
251#endif
252			}
253			break;
254
255			case kHFSPlusFileThreadRecord:
256			case kHFSPlusFolderThreadRecord:
257			{
258				if ( recordSize > sizeof(HFSPlusCatalogThread) || recordSize < (sizeof(HFSPlusCatalogThread) - sizeof(HFSUniStr255)))
259					return false;
260
261				cNodeID = catalogRecord->hfsPlusThread.parentID;
262				if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
263					return false;
264
265				if ( (catalogRecord->hfsPlusThread.nodeName.length == 0) ||
266					 (catalogRecord->hfsPlusThread.nodeName.length > 255) )
267					return false;
268			}
269			break;
270
271			default:
272				return false;
273		}
274	}
275
276	return true;	// record appears to be OK
277}
278