Sync with trunk 48067
[reactos.git] / drivers / filesystems / cdfs / fcb.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002, 2004 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * FILE: services/fs/cdfs/fcb.c
24 * PURPOSE: CDROM (ISO 9660) filesystem driver
25 * PROGRAMMER: Art Yerkes
26 * UPDATE HISTORY:
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include "cdfs.h"
32
33 #define NDEBUG
34 #include <debug.h>
35
36 /* FUNCTIONS ****************************************************************/
37
38 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
39
40
41 /* FUNCTIONS ****************************************************************/
42
43 static PWCHAR
44 CdfsGetNextPathElement(PWCHAR FileName)
45 {
46 if (*FileName == L'\0')
47 {
48 return(NULL);
49 }
50
51 while (*FileName != L'\0' && *FileName != L'\\')
52 {
53 FileName++;
54 }
55
56 return(FileName);
57 }
58
59
60 static VOID
61 CdfsWSubString(PWCHAR pTarget, const PWCHAR pSource, size_t pLength)
62 {
63 wcsncpy (pTarget, pSource, pLength);
64 pTarget [pLength] = L'\0';
65 }
66
67
68 PFCB
69 CdfsCreateFCB(PCWSTR FileName)
70 {
71 PFCB Fcb;
72
73 Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), TAG_FCB);
74 if(!Fcb) return NULL;
75
76 RtlZeroMemory(Fcb, sizeof(FCB));
77
78 if (FileName)
79 {
80 wcscpy(Fcb->PathName, FileName);
81 if (wcsrchr(Fcb->PathName, '\\') != 0)
82 {
83 Fcb->ObjectName = wcsrchr(Fcb->PathName, '\\');
84 }
85 else
86 {
87 Fcb->ObjectName = Fcb->PathName;
88 }
89 }
90
91 ExInitializeResourceLite(&Fcb->PagingIoResource);
92 ExInitializeResourceLite(&Fcb->MainResource);
93 ExInitializeResourceLite(&Fcb->NameListResource);
94 Fcb->RFCB.PagingIoResource = &Fcb->PagingIoResource;
95 Fcb->RFCB.Resource = &Fcb->MainResource;
96 Fcb->RFCB.IsFastIoPossible = FastIoIsNotPossible;
97 InitializeListHead(&Fcb->ShortNameList);
98
99 return(Fcb);
100 }
101
102
103 VOID
104 CdfsDestroyFCB(PFCB Fcb)
105 {
106 PLIST_ENTRY Entry;
107
108 ExDeleteResourceLite(&Fcb->PagingIoResource);
109 ExDeleteResourceLite(&Fcb->MainResource);
110
111 while (!IsListEmpty(&Fcb->ShortNameList))
112 {
113 Entry = Fcb->ShortNameList.Flink;
114 RemoveEntryList(Entry);
115 ExFreePoolWithTag(Entry, TAG_FCB);
116 }
117
118 ExDeleteResourceLite(&Fcb->NameListResource);
119 ExFreePoolWithTag(Fcb, TAG_FCB);
120 }
121
122
123 BOOLEAN
124 CdfsFCBIsDirectory(PFCB Fcb)
125 {
126 return(Fcb->Entry.FileFlags & FILE_FLAG_DIRECTORY);
127 }
128
129
130 BOOLEAN
131 CdfsFCBIsRoot(PFCB Fcb)
132 {
133 return(wcscmp(Fcb->PathName, L"\\") == 0);
134 }
135
136
137 VOID
138 CdfsGrabFCB(PDEVICE_EXTENSION Vcb,
139 PFCB Fcb)
140 {
141 KIRQL oldIrql;
142
143 DPRINT("grabbing FCB at %x: %S, refCount:%d\n",
144 Fcb,
145 Fcb->PathName,
146 Fcb->RefCount);
147
148 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
149 Fcb->RefCount++;
150 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
151 }
152
153
154 VOID
155 CdfsReleaseFCB(PDEVICE_EXTENSION Vcb,
156 PFCB Fcb)
157 {
158 KIRQL oldIrql;
159
160 DPRINT("releasing FCB at %x: %S, refCount:%d\n",
161 Fcb,
162 Fcb->PathName,
163 Fcb->RefCount);
164
165 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
166 Fcb->RefCount--;
167 if (Fcb->RefCount <= 0 && !CdfsFCBIsDirectory(Fcb))
168 {
169 RemoveEntryList(&Fcb->FcbListEntry);
170 CdfsDestroyFCB(Fcb);
171 }
172 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
173 }
174
175
176 VOID
177 CdfsAddFCBToTable(PDEVICE_EXTENSION Vcb,
178 PFCB Fcb)
179 {
180 KIRQL oldIrql;
181
182 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
183 Fcb->DevExt = Vcb;
184 InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry);
185 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
186 }
187
188
189 PFCB
190 CdfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb,
191 PUNICODE_STRING FileName)
192 {
193 KIRQL oldIrql;
194 PFCB Fcb;
195 PLIST_ENTRY current_entry;
196
197 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
198
199 if (FileName == NULL || FileName->Length == 0 || FileName->Buffer[0] == 0)
200 {
201 DPRINT("Return FCB for stream file object\n");
202 Fcb = Vcb->StreamFileObject->FsContext;
203 Fcb->RefCount++;
204 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
205 return(Fcb);
206 }
207
208 current_entry = Vcb->FcbListHead.Flink;
209 while (current_entry != &Vcb->FcbListHead)
210 {
211 Fcb = CONTAINING_RECORD(current_entry, FCB, FcbListEntry);
212
213 DPRINT("Comparing '%wZ' and '%S'\n", FileName, Fcb->PathName);
214 if (_wcsicmp(FileName->Buffer, Fcb->PathName) == 0)
215 {
216 Fcb->RefCount++;
217 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
218 return(Fcb);
219 }
220
221 //FIXME: need to compare against short name in FCB here
222
223 current_entry = current_entry->Flink;
224 }
225 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
226
227 return(NULL);
228 }
229
230
231 NTSTATUS
232 CdfsFCBInitializeCache(PVCB Vcb,
233 PFCB Fcb)
234 {
235 PFILE_OBJECT FileObject;
236 NTSTATUS Status;
237 PCCB newCCB;
238
239 FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice);
240
241 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
242 if (newCCB == NULL)
243 {
244 return(STATUS_INSUFFICIENT_RESOURCES);
245 }
246 RtlZeroMemory(newCCB,
247 sizeof(CCB));
248
249 FileObject->ReadAccess = TRUE;
250 FileObject->WriteAccess = FALSE;
251 FileObject->DeleteAccess = FALSE;
252 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
253 FileObject->FsContext = Fcb;
254 FileObject->FsContext2 = newCCB;
255 newCCB->PtrFileObject = FileObject;
256 Fcb->FileObject = FileObject;
257 Fcb->DevExt = Vcb;
258
259 Status = STATUS_SUCCESS;
260 CcInitializeCacheMap(FileObject,
261 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
262 FALSE,
263 &(CdfsGlobalData->CacheMgrCallbacks),
264 Fcb);
265
266 ObDereferenceObject(FileObject);
267 Fcb->Flags |= FCB_CACHE_INITIALIZED;
268
269 return(Status);
270 }
271
272
273 PFCB
274 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb)
275 {
276 PFCB Fcb;
277
278 Fcb = CdfsCreateFCB(L"\\");
279
280 Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
281 Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
282 Fcb->Entry.FileFlags = FILE_FLAG_DIRECTORY;
283 Fcb->IndexNumber.QuadPart = 0LL;
284 Fcb->RefCount = 1;
285 Fcb->DirIndex = 0;
286 Fcb->RFCB.FileSize.QuadPart = Vcb->CdInfo.RootSize;
287 Fcb->RFCB.ValidDataLength.QuadPart = Vcb->CdInfo.RootSize;
288 Fcb->RFCB.AllocationSize.QuadPart = Vcb->CdInfo.RootSize;
289
290 CdfsFCBInitializeCache(Vcb, Fcb);
291 CdfsAddFCBToTable(Vcb, Fcb);
292 CdfsGrabFCB(Vcb, Fcb);
293
294 return(Fcb);
295 }
296
297
298 PFCB
299 CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb)
300 {
301 UNICODE_STRING FileName;
302 PFCB Fcb;
303
304 RtlInitUnicodeString(&FileName, L"\\");
305
306 Fcb = CdfsGrabFCBFromTable(Vcb,
307 &FileName);
308 if (Fcb == NULL)
309 {
310 Fcb = CdfsMakeRootFCB(Vcb);
311 }
312
313 return(Fcb);
314 }
315
316
317 static VOID
318 CdfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt,
319 PDIR_RECORD Record,
320 PWSTR Name)
321 /*
322 * FUNCTION: Retrieves the file name from a directory record.
323 */
324 {
325 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
326 {
327 wcscpy(Name, L".");
328 }
329 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
330 {
331 wcscpy(Name, L"..");
332 }
333 else
334 {
335 if (DeviceExt->CdInfo.JolietLevel == 0)
336 {
337 ULONG i;
338
339 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
340 Name[i] = (WCHAR)Record->FileId[i];
341 Name[i] = 0;
342 }
343 else
344 {
345 CdfsSwapString(Name,
346 Record->FileId,
347 Record->FileIdLength);
348 }
349 }
350
351 DPRINT("Name '%S'\n", Name);
352 }
353
354
355 NTSTATUS
356 CdfsMakeFCBFromDirEntry(PVCB Vcb,
357 PFCB DirectoryFCB,
358 PWSTR LongName,
359 PWSTR ShortName,
360 PDIR_RECORD Record,
361 ULONG DirectorySector,
362 ULONG DirectoryOffset,
363 PFCB * fileFCB)
364 {
365 WCHAR pathName[MAX_PATH];
366 PFCB rcFCB;
367 ULONG Size;
368
369 if (LongName [0] != 0 && wcslen (DirectoryFCB->PathName) +
370 sizeof(WCHAR) + wcslen (LongName) > MAX_PATH)
371 {
372 return(STATUS_OBJECT_NAME_INVALID);
373 }
374
375 wcscpy(pathName, DirectoryFCB->PathName);
376 if (!CdfsFCBIsRoot(DirectoryFCB))
377 {
378 wcscat(pathName, L"\\");
379 }
380
381 if (LongName[0] != 0)
382 {
383 wcscat(pathName, LongName);
384 }
385 else
386 {
387 WCHAR entryName[MAX_PATH];
388
389 CdfsGetDirEntryName(Vcb, Record, entryName);
390 wcscat(pathName, entryName);
391 }
392
393 rcFCB = CdfsCreateFCB(pathName);
394 memcpy(&rcFCB->Entry, Record, sizeof(DIR_RECORD));
395
396 /* Copy short name into FCB */
397 rcFCB->ShortNameU.Length = wcslen(ShortName) * sizeof(WCHAR);
398 rcFCB->ShortNameU.MaximumLength = rcFCB->ShortNameU.Length;
399 rcFCB->ShortNameU.Buffer = rcFCB->ShortNameBuffer;
400 wcscpy(rcFCB->ShortNameBuffer, ShortName);
401
402 Size = rcFCB->Entry.DataLengthL;
403
404 rcFCB->RFCB.FileSize.QuadPart = Size;
405 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
406 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, BLOCKSIZE);
407 if (CdfsFCBIsDirectory(rcFCB))
408 {
409 CdfsFCBInitializeCache(Vcb, rcFCB);
410 }
411 rcFCB->IndexNumber.u.HighPart = DirectorySector;
412 rcFCB->IndexNumber.u.LowPart = DirectoryOffset;
413 rcFCB->RefCount++;
414 CdfsAddFCBToTable(Vcb, rcFCB);
415 *fileFCB = rcFCB;
416
417 DPRINT("%S %d %I64d\n", LongName, Size, rcFCB->RFCB.AllocationSize.QuadPart);
418
419 return(STATUS_SUCCESS);
420 }
421
422
423 NTSTATUS
424 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb,
425 PFCB Fcb,
426 PFILE_OBJECT FileObject)
427 {
428 PCCB newCCB;
429
430 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
431 if (newCCB == NULL)
432 {
433 return(STATUS_INSUFFICIENT_RESOURCES);
434 }
435 memset(newCCB, 0, sizeof(CCB));
436
437 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
438 FileObject->FsContext = Fcb;
439 FileObject->FsContext2 = newCCB;
440 newCCB->PtrFileObject = FileObject;
441 Fcb->DevExt = Vcb;
442
443 if (CdfsFCBIsDirectory(Fcb))
444 {
445 CcInitializeCacheMap(FileObject,
446 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
447 FALSE,
448 &(CdfsGlobalData->CacheMgrCallbacks),
449 Fcb);
450 Fcb->Flags |= FCB_CACHE_INITIALIZED;
451 }
452
453 DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
454
455 return(STATUS_SUCCESS);
456 }
457
458
459 NTSTATUS
460 CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt,
461 PFCB DirectoryFcb,
462 PUNICODE_STRING FileToFind,
463 PFCB *FoundFCB)
464 {
465 UNICODE_STRING TempName;
466 WCHAR Name[256];
467 PVOID Block;
468 ULONG DirSize;
469 PDIR_RECORD Record;
470 ULONG Offset;
471 ULONG BlockOffset;
472 NTSTATUS Status;
473
474 LARGE_INTEGER StreamOffset, OffsetOfEntry;
475 PVOID Context;
476
477 WCHAR ShortNameBuffer[13];
478 UNICODE_STRING ShortName;
479 UNICODE_STRING LongName;
480 UNICODE_STRING FileToFindUpcase;
481
482 ASSERT(DeviceExt);
483 ASSERT(DirectoryFcb);
484 ASSERT(FileToFind);
485
486 DPRINT("CdfsDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
487 DeviceExt,
488 DirectoryFcb,
489 FileToFind);
490 DPRINT("Dir Path:%S\n", DirectoryFcb->PathName);
491
492 /* default to '.' if no filename specified */
493 if (FileToFind->Length == 0)
494 {
495 RtlInitUnicodeString(&TempName, L".");
496 FileToFind = &TempName;
497 }
498
499 DirSize = DirectoryFcb->Entry.DataLengthL;
500 StreamOffset.QuadPart = (LONGLONG)DirectoryFcb->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
501
502 if (!CcMapData(DeviceExt->StreamFileObject,
503 &StreamOffset,
504 BLOCKSIZE,
505 TRUE,
506 &Context,
507 &Block))
508 {
509 DPRINT("CcMapData() failed\n");
510 return STATUS_UNSUCCESSFUL;
511 }
512
513 Offset = 0;
514 BlockOffset = 0;
515 Record = (PDIR_RECORD)Block;
516
517 /* Upper case the expression for FsRtlIsNameInExpression */
518 Status = RtlUpcaseUnicodeString(&FileToFindUpcase, FileToFind, TRUE);
519 if (!NT_SUCCESS(Status))
520 {
521 return Status;
522 }
523
524 while(TRUE)
525 {
526 if (Record->RecordLength == 0)
527 {
528 DPRINT("RecordLength == 0 Stopped!\n");
529 break;
530 }
531
532 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
533 Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
534
535 CdfsGetDirEntryName(DeviceExt, Record, Name);
536 DPRINT ("Name '%S'\n", Name);
537 DPRINT ("Sector %lu\n", DirectoryFcb->Entry.ExtentLocationL);
538 DPRINT ("Offset %lu\n", Offset);
539
540 RtlInitUnicodeString(&LongName, Name);
541 ShortName.Length = 0;
542 ShortName.MaximumLength = 26;
543 ShortName.Buffer = ShortNameBuffer;
544 memset(ShortNameBuffer, 0, 26);
545
546 OffsetOfEntry.QuadPart = StreamOffset.QuadPart + Offset;
547 CdfsShortNameCacheGet(DirectoryFcb, &OffsetOfEntry, &LongName, &ShortName);
548
549 DPRINT("ShortName '%wZ'\n", &ShortName);
550
551 if (FsRtlIsNameInExpression(&FileToFindUpcase, &LongName, TRUE, NULL) ||
552 FsRtlIsNameInExpression(&FileToFindUpcase, &ShortName, TRUE, NULL))
553 {
554 DPRINT("Match found, %S\n", Name);
555 Status = CdfsMakeFCBFromDirEntry(DeviceExt,
556 DirectoryFcb,
557 Name,
558 ShortNameBuffer,
559 Record,
560 DirectoryFcb->Entry.ExtentLocationL,
561 Offset,
562 FoundFCB);
563
564 RtlFreeUnicodeString(&FileToFindUpcase);
565 CcUnpinData(Context);
566
567 return(Status);
568 }
569
570 Offset += Record->RecordLength;
571 BlockOffset += Record->RecordLength;
572 Record = (PDIR_RECORD)((ULONG_PTR)Block + BlockOffset);
573 if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
574 {
575 DPRINT("Map next sector\n");
576 CcUnpinData(Context);
577 StreamOffset.QuadPart += BLOCKSIZE;
578 Offset = ROUND_UP(Offset, BLOCKSIZE);
579 BlockOffset = 0;
580
581 if (!CcMapData(DeviceExt->StreamFileObject,
582 &StreamOffset,
583 BLOCKSIZE, TRUE,
584 &Context, &Block))
585 {
586 DPRINT("CcMapData() failed\n");
587 RtlFreeUnicodeString(&FileToFindUpcase);
588 return(STATUS_UNSUCCESSFUL);
589 }
590 Record = (PDIR_RECORD)((ULONG_PTR)Block + BlockOffset);
591 }
592
593 if (Offset >= DirSize)
594 break;
595 }
596
597 RtlFreeUnicodeString(&FileToFindUpcase);
598 CcUnpinData(Context);
599
600 return(STATUS_OBJECT_NAME_NOT_FOUND);
601 }
602
603
604 NTSTATUS
605 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb,
606 PFCB *pParentFCB,
607 PFCB *pFCB,
608 PUNICODE_STRING FileName)
609 {
610 UNICODE_STRING PathName;
611 UNICODE_STRING ElementName;
612 NTSTATUS Status;
613 WCHAR pathName [MAX_PATH];
614 WCHAR elementName [MAX_PATH];
615 PWCHAR currentElement;
616 PFCB FCB;
617 PFCB parentFCB;
618
619 DPRINT("CdfsGetFCBForFile(%x, %x, %x, '%wZ')\n",
620 Vcb,
621 pParentFCB,
622 pFCB,
623 FileName);
624
625 /* Trivial case, open of the root directory on volume */
626 if (FileName->Buffer[0] == L'\0' || wcscmp(FileName->Buffer, L"\\") == 0)
627 {
628 DPRINT("returning root FCB\n");
629
630 FCB = CdfsOpenRootFCB(Vcb);
631 *pFCB = FCB;
632 *pParentFCB = NULL;
633
634 return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND);
635 }
636 else
637 {
638 currentElement = &FileName->Buffer[1];
639 wcscpy (pathName, L"\\");
640 FCB = CdfsOpenRootFCB (Vcb);
641 }
642 parentFCB = NULL;
643
644 /* Parse filename and check each path element for existance and access */
645 while (CdfsGetNextPathElement(currentElement) != 0)
646 {
647 /* Skip blank directory levels */
648 if ((CdfsGetNextPathElement(currentElement) - currentElement) == 0)
649 {
650 currentElement++;
651 continue;
652 }
653
654 DPRINT("Parsing, currentElement:%S\n", currentElement);
655 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB, FCB);
656
657 /* Descend to next directory level */
658 if (parentFCB)
659 {
660 CdfsReleaseFCB(Vcb, parentFCB);
661 parentFCB = NULL;
662 }
663
664 /* fail if element in FCB is not a directory */
665 if (!CdfsFCBIsDirectory(FCB))
666 {
667 DPRINT("Element in requested path is not a directory\n");
668
669 CdfsReleaseFCB(Vcb, FCB);
670 FCB = 0;
671 *pParentFCB = NULL;
672 *pFCB = NULL;
673
674 return(STATUS_OBJECT_PATH_NOT_FOUND);
675 }
676 parentFCB = FCB;
677
678 /* Extract next directory level into dirName */
679 CdfsWSubString(pathName,
680 FileName->Buffer,
681 CdfsGetNextPathElement(currentElement) - FileName->Buffer);
682 DPRINT(" pathName:%S\n", pathName);
683
684 RtlInitUnicodeString(&PathName, pathName);
685
686 FCB = CdfsGrabFCBFromTable(Vcb, &PathName);
687 if (FCB == NULL)
688 {
689 CdfsWSubString(elementName,
690 currentElement,
691 CdfsGetNextPathElement(currentElement) - currentElement);
692 DPRINT(" elementName:%S\n", elementName);
693
694 RtlInitUnicodeString(&ElementName, elementName);
695 Status = CdfsDirFindFile(Vcb,
696 parentFCB,
697 &ElementName,
698 &FCB);
699 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
700 {
701 *pParentFCB = parentFCB;
702 *pFCB = NULL;
703 currentElement = CdfsGetNextPathElement(currentElement);
704 if (*currentElement == L'\0' || CdfsGetNextPathElement(currentElement + 1) == 0)
705 {
706 return(STATUS_OBJECT_NAME_NOT_FOUND);
707 }
708 else
709 {
710 return(STATUS_OBJECT_PATH_NOT_FOUND);
711 }
712 }
713 else if (!NT_SUCCESS(Status))
714 {
715 CdfsReleaseFCB(Vcb, parentFCB);
716 *pParentFCB = NULL;
717 *pFCB = NULL;
718
719 return(Status);
720 }
721 }
722 currentElement = CdfsGetNextPathElement(currentElement);
723 }
724
725 *pParentFCB = parentFCB;
726 *pFCB = FCB;
727
728 return STATUS_SUCCESS;
729 }
730
731 /* EOF */