]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
9bccf70c | 2 | * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved. |
1c79356b A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
de355530 A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
1c79356b | 11 | * |
de355530 A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
de355530 A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
1c79356b A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
1c79356b A |
22 | #include <sys/param.h> |
23 | #include <sys/utfconv.h> | |
0b4e3aa0 | 24 | #include <sys/stat.h> |
1c79356b A |
25 | |
26 | #include "../headers/FileMgrInternal.h" | |
27 | #include "../headers/BTreesInternal.h" | |
28 | #include "../headers/CatalogPrivate.h" | |
29 | #include "../headers/HFSUnicodeWrappers.h" | |
30 | #include <string.h> | |
31 | ||
1c79356b A |
32 | |
33 | //******************************************************************************* | |
34 | // Routine: LocateCatalogNode | |
35 | // | |
36 | // Function: Locates the catalog record for an existing folder or file | |
37 | // CNode and returns pointers to the key and data records. | |
38 | // | |
39 | //******************************************************************************* | |
40 | ||
41 | OSErr | |
42 | LocateCatalogNode(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name, | |
43 | UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint) | |
44 | { | |
45 | OSErr result; | |
46 | CatalogName *nodeName = NULL; /* To ward off uninitialized use warnings from compiler */ | |
47 | HFSCatalogNodeID threadParentID; | |
48 | ||
49 | ||
50 | result = LocateCatalogRecord(volume, folderID, name, hint, keyPtr, dataPtr, newHint); | |
51 | ReturnIfError(result); | |
52 | ||
53 | // if we got a thread record, then go look up real record | |
54 | switch ( dataPtr->recordType ) | |
55 | { | |
56 | case kHFSFileThreadRecord: | |
57 | case kHFSFolderThreadRecord: | |
58 | threadParentID = dataPtr->hfsThread.parentID; | |
59 | nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName; | |
60 | break; | |
61 | ||
62 | case kHFSPlusFileThreadRecord: | |
63 | case kHFSPlusFolderThreadRecord: | |
64 | threadParentID = dataPtr->hfsPlusThread.parentID; | |
65 | nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName; | |
66 | break; | |
67 | ||
68 | default: | |
69 | threadParentID = 0; | |
70 | break; | |
71 | } | |
72 | ||
73 | if ( threadParentID ) // found a thread | |
74 | result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint); | |
75 | ||
76 | return result; | |
77 | } | |
78 | ||
79 | // | |
80 | // Routine: LocateCatalogNodeByKey | |
81 | // | |
82 | // Function: Locates the catalog record for an existing folder or file | |
83 | // CNode and returns the key and data records. | |
84 | // | |
85 | ||
86 | OSErr | |
87 | LocateCatalogNodeByKey(const ExtendedVCB *volume, UInt32 hint, CatalogKey *keyPtr, | |
88 | CatalogRecord *dataPtr, UInt32 *newHint) | |
89 | { | |
90 | OSErr result; | |
9bccf70c | 91 | CatalogName *nodeName = NULL; |
1c79356b A |
92 | HFSCatalogNodeID threadParentID; |
93 | UInt16 tempSize; | |
9bccf70c A |
94 | FSBufferDescriptor btRecord; |
95 | BTreeIterator searchIterator = {0}; | |
96 | FCB *fcb; | |
97 | ||
98 | fcb = GetFileControlBlock(volume->catalogRefNum); | |
1c79356b | 99 | |
9bccf70c A |
100 | btRecord.bufferAddress = dataPtr; |
101 | btRecord.itemCount = 1; | |
102 | btRecord.itemSize = sizeof(CatalogRecord); | |
103 | ||
104 | searchIterator.hint.nodeNum = hint; | |
105 | ||
106 | bcopy(keyPtr, &searchIterator.key, sizeof(CatalogKey)); | |
107 | ||
108 | result = BTSearchRecord( fcb, &searchIterator, &btRecord, &tempSize, &searchIterator ); | |
109 | ||
110 | if (result == noErr) | |
111 | { | |
112 | *newHint = searchIterator.hint.nodeNum; | |
113 | ||
114 | BlockMoveData(&searchIterator.key, keyPtr, sizeof(CatalogKey)); | |
115 | } | |
1c79356b | 116 | |
1c79356b A |
117 | if (result == btNotFound) |
118 | result = cmNotFound; | |
119 | ReturnIfError(result); | |
120 | ||
121 | // if we got a thread record, then go look up real record | |
122 | switch ( dataPtr->recordType ) | |
123 | { | |
124 | case kHFSFileThreadRecord: | |
125 | case kHFSFolderThreadRecord: | |
126 | threadParentID = dataPtr->hfsThread.parentID; | |
127 | nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName; | |
128 | break; | |
129 | ||
130 | case kHFSPlusFileThreadRecord: | |
131 | case kHFSPlusFolderThreadRecord: | |
132 | threadParentID = dataPtr->hfsPlusThread.parentID; | |
133 | nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName; | |
134 | break; | |
135 | ||
136 | default: | |
137 | threadParentID = 0; | |
138 | break; | |
139 | } | |
140 | ||
141 | if ( threadParentID ) // found a thread | |
142 | result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint); | |
143 | ||
144 | return result; | |
145 | } | |
146 | ||
147 | ||
1c79356b A |
148 | |
149 | //******************************************************************************* | |
150 | // Routine: LocateCatalogRecord | |
151 | // | |
152 | // Function: Locates the catalog record associated with folderID and name | |
153 | // | |
154 | //******************************************************************************* | |
155 | ||
156 | OSErr | |
157 | LocateCatalogRecord(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name, | |
158 | UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint) | |
159 | { | |
160 | OSErr result; | |
161 | CatalogKey tempKey; // 518 bytes | |
162 | UInt16 tempSize; | |
163 | ||
164 | BuildCatalogKey(folderID, name, (volume->vcbSigWord == kHFSPlusSigWord), &tempKey); | |
165 | ||
166 | if ( name == NULL ) | |
167 | hint = kNoHint; // no CName given so clear the hint | |
168 | ||
169 | result = SearchBTreeRecord(volume->catalogRefNum, &tempKey, hint, keyPtr, dataPtr, &tempSize, newHint); | |
170 | ||
171 | return (result == btNotFound ? cmNotFound : result); | |
172 | } | |
173 | ||
174 | ||
1c79356b A |
175 | |
176 | /* | |
177 | * Routine: BuildCatalogKey | |
178 | * | |
179 | * Function: Constructs a catalog key record (ckr) given the parent | |
180 | * folder ID and CName. Works for both classic and extended | |
181 | * HFS volumes. | |
182 | * | |
183 | */ | |
184 | ||
185 | void | |
186 | BuildCatalogKey(HFSCatalogNodeID parentID, const CatalogName *cName, Boolean isHFSPlus, CatalogKey *key) | |
187 | { | |
188 | if ( isHFSPlus ) | |
189 | { | |
190 | key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; // initial key length (4 + 2) | |
191 | key->hfsPlus.parentID = parentID; // set parent ID | |
192 | key->hfsPlus.nodeName.length = 0; // null CName length | |
193 | if ( cName != NULL ) | |
194 | { | |
195 | CopyCatalogName(cName, (CatalogName *) &key->hfsPlus.nodeName, isHFSPlus); | |
196 | key->hfsPlus.keyLength += sizeof(UniChar) * cName->ustr.length; // add CName size to key length | |
197 | } | |
198 | } | |
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 | } | |
212 | ||
1c79356b A |
213 | OSErr |
214 | BuildCatalogKeyUTF8(ExtendedVCB *volume, HFSCatalogNodeID parentID, const char *name, UInt32 nameLength, | |
215 | CatalogKey *key, UInt32 *textEncoding) | |
216 | { | |
217 | OSErr err = 0; | |
218 | ||
219 | if ( name == NULL) | |
220 | nameLength = 0; | |
221 | else if (nameLength == kUndefinedStrLen) | |
222 | nameLength = strlen(name); | |
223 | ||
224 | if ( volume->vcbSigWord == kHFSPlusSigWord ) { | |
225 | size_t unicodeBytes = 0; | |
226 | ||
227 | key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; // initial key length (4 + 2) | |
228 | key->hfsPlus.parentID = parentID; // set parent ID | |
229 | key->hfsPlus.nodeName.length = 0; // null CName length | |
230 | if ( nameLength > 0 ) { | |
231 | err = utf8_decodestr(name, nameLength, key->hfsPlus.nodeName.unicode, | |
232 | &unicodeBytes, sizeof(key->hfsPlus.nodeName.unicode), ':', UTF_DECOMPOSED); | |
233 | key->hfsPlus.nodeName.length = unicodeBytes / sizeof(UniChar); | |
234 | key->hfsPlus.keyLength += unicodeBytes; | |
235 | } | |
236 | ||
0b4e3aa0 A |
237 | if (textEncoding && (*textEncoding != kTextEncodingMacUnicode)) |
238 | *textEncoding = hfs_pickencoding(key->hfsPlus.nodeName.unicode, | |
239 | key->hfsPlus.nodeName.length); | |
1c79356b A |
240 | } |
241 | else { | |
242 | key->hfs.keyLength = kHFSCatalogKeyMinimumLength; // initial key length (1 + 4 + 1) | |
243 | key->hfs.reserved = 0; // clear unused byte | |
244 | key->hfs.parentID = parentID; // set parent ID | |
245 | key->hfs.nodeName[0] = 0; // null CName length | |
246 | if ( nameLength > 0 ) { | |
247 | err = utf8_to_hfs(volume, nameLength, name, &key->hfs.nodeName[0]); | |
248 | /* | |
249 | * Retry with MacRoman in case that's how it was exported. | |
250 | * When textEncoding != NULL we know that this is a create | |
251 | * or rename call and can skip the retry (ugly but it works). | |
252 | */ | |
253 | if (err && (textEncoding == NULL)) | |
254 | err = utf8_to_mac_roman(nameLength, name, &key->hfs.nodeName[0]); | |
255 | key->hfs.keyLength += key->hfs.nodeName[0]; // add CName size to key length | |
256 | } | |
257 | if (textEncoding) | |
258 | *textEncoding = 0; | |
259 | } | |
260 | ||
261 | if (err) { | |
262 | if (err == ENAMETOOLONG) | |
263 | err = bdNamErr; /* name is too long */ | |
264 | else | |
265 | err = paramErr; /* name has invalid characters */ | |
266 | } | |
267 | ||
268 | return err; | |
269 | } | |
270 | ||
271 | ||
1c79356b A |
272 | //******************************************************************************* |
273 | // Routine: FlushCatalog | |
274 | // | |
275 | // Function: Flushes the catalog for a specified volume. | |
276 | // | |
277 | //******************************************************************************* | |
278 | ||
279 | OSErr | |
280 | FlushCatalog(ExtendedVCB *volume) | |
281 | { | |
282 | FCB * fcb; | |
283 | OSErr result; | |
284 | ||
285 | fcb = GetFileControlBlock(volume->catalogRefNum); | |
286 | result = BTFlushPath(fcb); | |
287 | ||
288 | if (result == noErr) | |
289 | { | |
290 | //--- check if catalog's fcb is dirty... | |
291 | ||
9bccf70c | 292 | if ( 0 /*fcb->fcbFlags & fcbModifiedMask*/ ) |
1c79356b A |
293 | { |
294 | VCB_LOCK(volume); | |
295 | volume->vcbFlags |= 0xFF00; // Mark the VCB dirty | |
296 | volume->vcbLsMod = GetTimeUTC(); // update last modified date | |
297 | VCB_UNLOCK(volume); | |
298 | ||
9bccf70c | 299 | // result = FlushVolumeControlBlock(volume); |
1c79356b A |
300 | } |
301 | } | |
302 | ||
303 | return result; | |
304 | } | |
305 | ||
306 | ||
307 |