1/*
2 * Copyright (c) 1999-2000, 2002, 2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "Scavenger.h"
25
26
27OSErr FlushCatalogFile( SVCB *vcb )
28{
29	OSErr	err;
30
31	err = BTFlushPath(vcb->vcbCatalogFile);
32	if ( err == noErr )
33	{
34		if( ( vcb->vcbCatalogFile->fcbFlags & fcbModifiedMask ) != 0 )
35		{
36			(void) MarkVCBDirty( vcb );
37			err = FlushVolumeControlBlock( vcb );
38		}
39	}
40
41	return( err );
42}
43
44OSErr LocateCatalogNode(SFCB *fcb, BTreeIterator *iterator, FSBufferDescriptor *btRecord, UInt16 *reclen)
45{
46	CatalogRecord * recp;
47	CatalogKey * keyp;
48	CatalogName * namep = NULL;
49	UInt32 threadpid = 0;
50	OSErr result;
51	Boolean isHFSPlus = false;
52
53	result = BTSearchRecord(fcb, iterator, kInvalidMRUCacheKey, btRecord, reclen, iterator);
54	if (result == btNotFound)
55		result = cmNotFound;
56	ReturnIfError(result);
57
58	recp = (CatalogRecord *)btRecord->bufferAddress;
59	keyp = (CatalogKey*)&iterator->key;
60
61	/* if we got a thread record, then go look up real record */
62	switch (recp->recordType) {
63	case kHFSFileThreadRecord:
64	case kHFSFolderThreadRecord:
65		threadpid = recp->hfsThread.parentID;
66		namep = (CatalogName *) &recp->hfsThread.nodeName;
67		isHFSPlus = false;
68		break;
69
70	case kHFSPlusFileThreadRecord:
71	case kHFSPlusFolderThreadRecord:
72		threadpid = recp->hfsPlusThread.parentID;
73		namep = (CatalogName *) &recp->hfsPlusThread.nodeName;
74		isHFSPlus = true;
75		break;
76
77	default:
78		threadpid = 0;
79		break;
80	}
81
82	if (threadpid) {
83		(void) BTInvalidateHint(iterator);
84		BuildCatalogKey(threadpid, namep, isHFSPlus, keyp);
85		result = BTSearchRecord(fcb, iterator, kInvalidMRUCacheKey, btRecord, reclen, iterator);
86	}
87
88	return result;
89}
90
91
92OSErr
93UpdateFolderCount(SVCB *vcb, HFSCatalogNodeID pid, const CatalogName *name, SInt16 newType,
94					UInt32 hint, SInt16 valenceDelta)
95{
96	CatalogRecord		tempData;	// 520 bytes
97	HFSCatalogNodeID	folderID;
98	UInt16				reclen;
99	OSErr				result;
100	BTreeIterator btIterator;
101	FSBufferDescriptor btRecord;
102
103	btRecord.bufferAddress = &tempData;
104	btRecord.itemCount = 1;
105	btRecord.itemSize = sizeof(tempData);
106
107	ClearMemory(&btIterator, sizeof(btIterator));
108	btIterator.hint.nodeNum = hint;
109	BuildCatalogKey(pid, name, vcb->vcbSignature == kHFSPlusSigWord, (CatalogKey*)&btIterator.key);
110	result = LocateCatalogNode(vcb->vcbCatalogFile, &btIterator, &btRecord, &reclen);
111	ReturnIfError(result);
112
113	if (vcb->vcbSignature == kHFSPlusSigWord) {
114		UInt32		timeStamp;
115
116		timeStamp = GetTimeUTC();
117		tempData.hfsPlusFolder.valence += valenceDelta;		// adjust valence
118		tempData.hfsPlusFolder.contentModDate = timeStamp;	// set date/time last modified
119		folderID = tempData.hfsPlusFolder.folderID;
120	} else /* kHFSSigWord */ {
121		tempData.hfsFolder.valence += valenceDelta;				// adjust valence
122		tempData.hfsFolder.modifyDate = GetTimeLocal(true);		// set date/time last modified
123		folderID = tempData.hfsFolder.folderID;
124	}
125
126	result = BTReplaceRecord(vcb->vcbCatalogFile, &btIterator, &btRecord, reclen);
127	ReturnIfError(result);
128
129	if (folderID == kHFSRootFolderID) {
130		if (newType == kHFSFolderRecord || newType == kHFSPlusFolderRecord)
131			vcb->vcbNmRtDirs += valenceDelta;	// adjust root folder count (undefined for HFS Plus)
132		else
133			vcb->vcbNmFls += valenceDelta;		// adjust root file count (used by GetVolInfo)
134	}
135
136	if (newType == kHFSFolderRecord || newType == kHFSPlusFolderRecord)
137		vcb->vcbFolderCount += valenceDelta;			// adjust volume folder count, �� worry about overflow?
138	else
139		vcb->vcbFileCount += valenceDelta;			// adjust volume file count
140
141	vcb->vcbModifyDate = GetTimeUTC();	// update last modified date
142	MarkVCBDirty( vcb );
143
144	return result;
145}
146
147
148/* Delete the catalog node with given name from given parent directory.
149 * The boolean value for_rename indicates that the caller is interested
150 * in deleting this record as part of rename operation and hence when set
151 * to true, the function does not return error if the directory record
152 * being deleted has non-zero valence and does not deallocate blocks for given
153 * file.
154 */
155OSErr
156DeleteCatalogNode(SVCB *vcb, UInt32 pid, const CatalogName * name, UInt32 hint, Boolean for_rename)
157{
158	CatalogKey * keyp;
159	CatalogRecord rec;
160	BTreeIterator btIterator;
161	FSBufferDescriptor btRecord;
162
163	HFSCatalogNodeID	nodeID;
164	HFSCatalogNodeID	nodeParentID;
165	UInt16				nodeType;
166	UInt16 reclen;
167	OSErr result;
168	Boolean isHFSPlus = (vcb->vcbSignature == kHFSPlusSigWord);
169
170	btRecord.bufferAddress = &rec;
171	btRecord.itemCount = 1;
172	btRecord.itemSize = sizeof(rec);
173
174	ClearMemory(&btIterator, sizeof(btIterator));
175	btIterator.hint.nodeNum = hint;
176	keyp = (CatalogKey*)&btIterator.key;
177	BuildCatalogKey(pid, name, isHFSPlus, keyp);
178
179	result = LocateCatalogNode(vcb->vcbCatalogFile, &btIterator, &btRecord, &reclen);
180	ReturnIfError(result);
181
182	/* establish real parent cnid  and cnode type */
183	nodeParentID = isHFSPlus ? keyp->hfsPlus.parentID : keyp->hfs.parentID;
184	nodeType = rec.recordType;
185	nodeID = 0;
186
187	switch (nodeType) {
188	case kHFSFolderRecord:
189		if ((for_rename == false) && (rec.hfsFolder.valence != 0))
190			return cmNotEmpty;
191
192		nodeID = rec.hfsFolder.folderID;
193		break;
194
195	case kHFSPlusFolderRecord:
196		if ((for_rename == false) && (rec.hfsPlusFolder.valence != 0))
197			return cmNotEmpty;
198
199		nodeID = rec.hfsPlusFolder.folderID;
200		break;
201
202	case kHFSFileRecord:
203		if (rec.hfsFile.flags & kHFSThreadExistsMask)
204			nodeID = rec.hfsFile.fileID;
205		break;
206
207	case kHFSPlusFileRecord:
208		nodeID = rec.hfsPlusFile.fileID;
209		break;
210
211	default:
212		return cmNotFound;
213	}
214
215	if (nodeID == kHFSRootFolderID)
216		return cmRootCN;  /* sorry, you can't delete the root! */
217
218	/* delete catalog records for CNode and thread */
219	result = BTDeleteRecord(vcb->vcbCatalogFile, &btIterator);
220	ReturnIfError(result);
221
222	(void) BTInvalidateHint(&btIterator);
223
224	if ( nodeID ) {
225		BuildCatalogKey(nodeID, NULL, isHFSPlus, keyp);
226		(void) BTDeleteRecord(vcb->vcbCatalogFile, &btIterator);
227	}
228
229	/* update directory and volume stats */
230
231	result = UpdateFolderCount(vcb, nodeParentID, NULL, nodeType, kNoHint, -1);
232	ReturnIfError(result);
233
234	(void) FlushCatalogFile(vcb);
235
236	if (((nodeType == kHFSPlusFileRecord) || (nodeType == kHFSFileRecord)) &&
237	    (for_rename == false))
238		result = DeallocateFile(vcb, &rec);
239
240	return result;
241}
242
243
244OSErr
245GetCatalogNode(SVCB *vcb, UInt32 pid, const CatalogName * name, UInt32 hint, CatalogRecord *data)
246{
247	CatalogKey * keyp;
248	BTreeIterator btIterator;
249	FSBufferDescriptor btRecord;
250
251	UInt16 reclen;
252	OSErr result;
253	Boolean isHFSPlus = (vcb->vcbSignature == kHFSPlusSigWord);
254
255	btRecord.bufferAddress = data;
256	btRecord.itemCount = 1;
257	btRecord.itemSize = sizeof(CatalogRecord);
258
259	ClearMemory(&btIterator, sizeof(btIterator));
260	btIterator.hint.nodeNum = hint;
261	keyp = (CatalogKey*)&btIterator.key;
262	BuildCatalogKey(pid, name, isHFSPlus, keyp);
263
264	result = LocateCatalogNode(vcb->vcbCatalogFile, &btIterator, &btRecord, &reclen);
265
266	return result;
267}
268
269