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