[MSI]
[reactos.git] / reactos / 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 PCCB newCCB;
237
238 FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice);
239
240 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
241 if (newCCB == NULL)
242 {
243 return STATUS_INSUFFICIENT_RESOURCES;
244 }
245 RtlZeroMemory(newCCB,
246 sizeof(CCB));
247
248 FileObject->ReadAccess = TRUE;
249 FileObject->WriteAccess = FALSE;
250 FileObject->DeleteAccess = FALSE;
251 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
252 FileObject->FsContext = Fcb;
253 FileObject->FsContext2 = newCCB;
254 newCCB->PtrFileObject = FileObject;
255 Fcb->FileObject = FileObject;
256 Fcb->DevExt = Vcb;
257
258 CcInitializeCacheMap(FileObject,
259 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
260 FALSE,
261 &(CdfsGlobalData->CacheMgrCallbacks),
262 Fcb);
263
264 ObDereferenceObject(FileObject);
265 Fcb->Flags |= FCB_CACHE_INITIALIZED;
266
267 return STATUS_SUCCESS;
268 }
269
270
271 PFCB
272 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb)
273 {
274 PFCB Fcb;
275
276 Fcb = CdfsCreateFCB(L"\\");
277
278 Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
279 Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
280 Fcb->Entry.FileFlags = FILE_FLAG_DIRECTORY;
281 Fcb->IndexNumber.QuadPart = 0LL;
282 Fcb->RefCount = 1;
283 Fcb->DirIndex = 0;
284 Fcb->RFCB.FileSize.QuadPart = Vcb->CdInfo.RootSize;
285 Fcb->RFCB.ValidDataLength.QuadPart = Vcb->CdInfo.RootSize;
286 Fcb->RFCB.AllocationSize.QuadPart = Vcb->CdInfo.RootSize;
287
288 CdfsFCBInitializeCache(Vcb, Fcb);
289 CdfsAddFCBToTable(Vcb, Fcb);
290 CdfsGrabFCB(Vcb, Fcb);
291
292 return(Fcb);
293 }
294
295
296 PFCB
297 CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb)
298 {
299 UNICODE_STRING FileName;
300 PFCB Fcb;
301
302 RtlInitUnicodeString(&FileName, L"\\");
303
304 Fcb = CdfsGrabFCBFromTable(Vcb,
305 &FileName);
306 if (Fcb == NULL)
307 {
308 Fcb = CdfsMakeRootFCB(Vcb);
309 }
310
311 return(Fcb);
312 }
313
314
315 static VOID
316 CdfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt,
317 PDIR_RECORD Record,
318 PWSTR Name)
319 /*
320 * FUNCTION: Retrieves the file name from a directory record.
321 */
322 {
323 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
324 {
325 wcscpy(Name, L".");
326 }
327 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
328 {
329 wcscpy(Name, L"..");
330 }
331 else
332 {
333 if (DeviceExt->CdInfo.JolietLevel == 0)
334 {
335 ULONG i;
336
337 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
338 Name[i] = (WCHAR)Record->FileId[i];
339 Name[i] = 0;
340 }
341 else
342 {
343 CdfsSwapString(Name,
344 Record->FileId,
345 Record->FileIdLength);
346 }
347 }
348
349 DPRINT("Name '%S'\n", Name);
350 }
351
352
353 NTSTATUS
354 CdfsMakeFCBFromDirEntry(PVCB Vcb,
355 PFCB DirectoryFCB,
356 PWSTR LongName,
357 PWSTR ShortName,
358 PDIR_RECORD Record,
359 ULONG DirectorySector,
360 ULONG DirectoryOffset,
361 PFCB * fileFCB)
362 {
363 WCHAR pathName[MAX_PATH];
364 PFCB rcFCB;
365 ULONG Size;
366
367 if (LongName [0] != 0 && wcslen (DirectoryFCB->PathName) +
368 sizeof(WCHAR) + wcslen (LongName) > MAX_PATH)
369 {
370 return(STATUS_OBJECT_NAME_INVALID);
371 }
372
373 wcscpy(pathName, DirectoryFCB->PathName);
374 if (!CdfsFCBIsRoot(DirectoryFCB))
375 {
376 wcscat(pathName, L"\\");
377 }
378
379 if (LongName[0] != 0)
380 {
381 wcscat(pathName, LongName);
382 }
383 else
384 {
385 WCHAR entryName[MAX_PATH];
386
387 CdfsGetDirEntryName(Vcb, Record, entryName);
388 wcscat(pathName, entryName);
389 }
390
391 rcFCB = CdfsCreateFCB(pathName);
392 memcpy(&rcFCB->Entry, Record, sizeof(DIR_RECORD));
393
394 /* Copy short name into FCB */
395 rcFCB->ShortNameU.Length = wcslen(ShortName) * sizeof(WCHAR);
396 rcFCB->ShortNameU.MaximumLength = rcFCB->ShortNameU.Length;
397 rcFCB->ShortNameU.Buffer = rcFCB->ShortNameBuffer;
398 wcscpy(rcFCB->ShortNameBuffer, ShortName);
399
400 Size = rcFCB->Entry.DataLengthL;
401
402 rcFCB->RFCB.FileSize.QuadPart = Size;
403 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
404 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, BLOCKSIZE);
405 if (CdfsFCBIsDirectory(rcFCB))
406 {
407 CdfsFCBInitializeCache(Vcb, rcFCB);
408 }
409 rcFCB->IndexNumber.u.HighPart = DirectorySector;
410 rcFCB->IndexNumber.u.LowPart = DirectoryOffset;
411 rcFCB->RefCount++;
412 CdfsAddFCBToTable(Vcb, rcFCB);
413 *fileFCB = rcFCB;
414
415 DPRINT("%S %d %I64d\n", LongName, Size, rcFCB->RFCB.AllocationSize.QuadPart);
416
417 return(STATUS_SUCCESS);
418 }
419
420
421 NTSTATUS
422 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb,
423 PFCB Fcb,
424 PFILE_OBJECT FileObject)
425 {
426 PCCB newCCB;
427
428 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
429 if (newCCB == NULL)
430 {
431 return(STATUS_INSUFFICIENT_RESOURCES);
432 }
433 memset(newCCB, 0, sizeof(CCB));
434
435 FileObject->ReadAccess = TRUE;
436 FileObject->WriteAccess = FALSE;
437 FileObject->DeleteAccess = FALSE;
438 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
439 FileObject->FsContext = Fcb;
440 FileObject->FsContext2 = newCCB;
441 newCCB->PtrFileObject = FileObject;
442 Fcb->DevExt = Vcb;
443
444 if (CdfsFCBIsDirectory(Fcb))
445 {
446 CcInitializeCacheMap(FileObject,
447 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
448 FALSE,
449 &(CdfsGlobalData->CacheMgrCallbacks),
450 Fcb);
451 Fcb->Flags |= FCB_CACHE_INITIALIZED;
452 }
453
454 DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
455
456 return(STATUS_SUCCESS);
457 }
458
459
460 NTSTATUS
461 CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt,
462 PFCB DirectoryFcb,
463 PUNICODE_STRING FileToFind,
464 PFCB *FoundFCB)
465 {
466 UNICODE_STRING TempName;
467 WCHAR Name[256];
468 PVOID Block;
469 ULONG DirSize;
470 PDIR_RECORD Record;
471 ULONG Offset;
472 ULONG BlockOffset;
473 NTSTATUS Status;
474
475 LARGE_INTEGER StreamOffset, OffsetOfEntry;
476 PVOID Context;
477
478 WCHAR ShortNameBuffer[13];
479 UNICODE_STRING ShortName;
480 UNICODE_STRING LongName;
481 UNICODE_STRING FileToFindUpcase;
482
483 ASSERT(DeviceExt);
484 ASSERT(DirectoryFcb);
485 ASSERT(FileToFind);
486
487 DPRINT("CdfsDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
488 DeviceExt,
489 DirectoryFcb,
490 FileToFind);
491 DPRINT("Dir Path:%S\n", DirectoryFcb->PathName);
492
493 /* default to '.' if no filename specified */
494 if (FileToFind->Length == 0)
495 {
496 RtlInitUnicodeString(&TempName, L".");
497 FileToFind = &TempName;
498 }
499
500 DirSize = DirectoryFcb->Entry.DataLengthL;
501 StreamOffset.QuadPart = (LONGLONG)DirectoryFcb->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
502
503 if (!CcMapData(DeviceExt->StreamFileObject,
504 &StreamOffset,
505 BLOCKSIZE,
506 TRUE,
507 &Context,
508 &Block))
509 {
510 DPRINT("CcMapData() failed\n");
511 return STATUS_UNSUCCESSFUL;
512 }
513
514 Offset = 0;
515 BlockOffset = 0;
516 Record = (PDIR_RECORD)Block;
517
518 /* Upper case the expression for FsRtlIsNameInExpression */
519 Status = RtlUpcaseUnicodeString(&FileToFindUpcase, FileToFind, TRUE);
520 if (!NT_SUCCESS(Status))
521 {
522 return Status;
523 }
524
525 while(TRUE)
526 {
527 if (Record->RecordLength == 0)
528 {
529 DPRINT("RecordLength == 0 Stopped!\n");
530 break;
531 }
532
533 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
534 Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
535
536 CdfsGetDirEntryName(DeviceExt, Record, Name);
537 DPRINT ("Name '%S'\n", Name);
538 DPRINT ("Sector %lu\n", DirectoryFcb->Entry.ExtentLocationL);
539 DPRINT ("Offset %lu\n", Offset);
540
541 RtlInitUnicodeString(&LongName, Name);
542 ShortName.Length = 0;
543 ShortName.MaximumLength = 26;
544 ShortName.Buffer = ShortNameBuffer;
545 memset(ShortNameBuffer, 0, 26);
546
547 OffsetOfEntry.QuadPart = StreamOffset.QuadPart + Offset;
548 CdfsShortNameCacheGet(DirectoryFcb, &OffsetOfEntry, &LongName, &ShortName);
549
550 DPRINT("ShortName '%wZ'\n", &ShortName);
551
552 if (FsRtlIsNameInExpression(&FileToFindUpcase, &LongName, TRUE, NULL) ||
553 FsRtlIsNameInExpression(&FileToFindUpcase, &ShortName, TRUE, NULL))
554 {
555 DPRINT("Match found, %S\n", Name);
556 Status = CdfsMakeFCBFromDirEntry(DeviceExt,
557 DirectoryFcb,
558 Name,
559 ShortNameBuffer,
560 Record,
561 DirectoryFcb->Entry.ExtentLocationL,
562 Offset,
563 FoundFCB);
564
565 RtlFreeUnicodeString(&FileToFindUpcase);
566 CcUnpinData(Context);
567
568 return(Status);
569 }
570
571 Offset += Record->RecordLength;
572 BlockOffset += Record->RecordLength;
573 Record = (PDIR_RECORD)((ULONG_PTR)Block + BlockOffset);
574 if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
575 {
576 DPRINT("Map next sector\n");
577 CcUnpinData(Context);
578 StreamOffset.QuadPart += BLOCKSIZE;
579 Offset = ROUND_UP(Offset, BLOCKSIZE);
580 BlockOffset = 0;
581
582 if (!CcMapData(DeviceExt->StreamFileObject,
583 &StreamOffset,
584 BLOCKSIZE, TRUE,
585 &Context, &Block))
586 {
587 DPRINT("CcMapData() failed\n");
588 RtlFreeUnicodeString(&FileToFindUpcase);
589 return(STATUS_UNSUCCESSFUL);
590 }
591 Record = (PDIR_RECORD)((ULONG_PTR)Block + BlockOffset);
592 }
593
594 if (Offset >= DirSize)
595 break;
596 }
597
598 RtlFreeUnicodeString(&FileToFindUpcase);
599 CcUnpinData(Context);
600
601 return(STATUS_OBJECT_NAME_NOT_FOUND);
602 }
603
604
605 NTSTATUS
606 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb,
607 PFCB *pParentFCB,
608 PFCB *pFCB,
609 PUNICODE_STRING FileName)
610 {
611 UNICODE_STRING PathName;
612 UNICODE_STRING ElementName;
613 NTSTATUS Status;
614 WCHAR pathName [MAX_PATH];
615 WCHAR elementName [MAX_PATH];
616 PWCHAR currentElement;
617 PFCB FCB;
618 PFCB parentFCB;
619
620 DPRINT("CdfsGetFCBForFile(%x, %x, %x, '%wZ')\n",
621 Vcb,
622 pParentFCB,
623 pFCB,
624 FileName);
625
626 /* Trivial case, open of the root directory on volume */
627 if (FileName->Buffer[0] == L'\0' || wcscmp(FileName->Buffer, L"\\") == 0)
628 {
629 DPRINT("returning root FCB\n");
630
631 FCB = CdfsOpenRootFCB(Vcb);
632 *pFCB = FCB;
633 *pParentFCB = NULL;
634
635 return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND);
636 }
637 else
638 {
639 currentElement = &FileName->Buffer[1];
640 wcscpy (pathName, L"\\");
641 FCB = CdfsOpenRootFCB (Vcb);
642 }
643 parentFCB = NULL;
644
645 /* Parse filename and check each path element for existance and access */
646 while (CdfsGetNextPathElement(currentElement) != 0)
647 {
648 /* Skip blank directory levels */
649 if ((CdfsGetNextPathElement(currentElement) - currentElement) == 0)
650 {
651 currentElement++;
652 continue;
653 }
654
655 DPRINT("Parsing, currentElement:%S\n", currentElement);
656 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB, FCB);
657
658 /* Descend to next directory level */
659 if (parentFCB)
660 {
661 CdfsReleaseFCB(Vcb, parentFCB);
662 parentFCB = NULL;
663 }
664
665 /* fail if element in FCB is not a directory */
666 if (!CdfsFCBIsDirectory(FCB))
667 {
668 DPRINT("Element in requested path is not a directory\n");
669
670 CdfsReleaseFCB(Vcb, FCB);
671 FCB = 0;
672 *pParentFCB = NULL;
673 *pFCB = NULL;
674
675 return(STATUS_OBJECT_PATH_NOT_FOUND);
676 }
677 parentFCB = FCB;
678
679 /* Extract next directory level into dirName */
680 CdfsWSubString(pathName,
681 FileName->Buffer,
682 CdfsGetNextPathElement(currentElement) - FileName->Buffer);
683 DPRINT(" pathName:%S\n", pathName);
684
685 RtlInitUnicodeString(&PathName, pathName);
686
687 FCB = CdfsGrabFCBFromTable(Vcb, &PathName);
688 if (FCB == NULL)
689 {
690 CdfsWSubString(elementName,
691 currentElement,
692 CdfsGetNextPathElement(currentElement) - currentElement);
693 DPRINT(" elementName:%S\n", elementName);
694
695 RtlInitUnicodeString(&ElementName, elementName);
696 Status = CdfsDirFindFile(Vcb,
697 parentFCB,
698 &ElementName,
699 &FCB);
700 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
701 {
702 *pParentFCB = parentFCB;
703 *pFCB = NULL;
704 currentElement = CdfsGetNextPathElement(currentElement);
705 if (*currentElement == L'\0' || CdfsGetNextPathElement(currentElement + 1) == 0)
706 {
707 return(STATUS_OBJECT_NAME_NOT_FOUND);
708 }
709 else
710 {
711 return(STATUS_OBJECT_PATH_NOT_FOUND);
712 }
713 }
714 else if (!NT_SUCCESS(Status))
715 {
716 CdfsReleaseFCB(Vcb, parentFCB);
717 *pParentFCB = NULL;
718 *pFCB = NULL;
719
720 return(Status);
721 }
722 }
723 currentElement = CdfsGetNextPathElement(currentElement);
724 }
725
726 *pParentFCB = parentFCB;
727 *pFCB = FCB;
728
729 return STATUS_SUCCESS;
730 }
731
732 /* EOF */