Fixed FCB management functions.
[reactos.git] / reactos / drivers / fs / cdfs / fcb.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002 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: fcb.c,v 1.4 2002/05/01 13:15:42 ekohl Exp $
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 <ddk/ntddk.h>
32
33 #define NDEBUG
34 #include <debug.h>
35
36 #include "cdfs.h"
37
38
39 /* MACROS *******************************************************************/
40
41 #define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
42 #define TAG_FCB TAG('I', 'F', 'C', 'B')
43
44 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
45
46
47 /* FUNCTIONS ****************************************************************/
48
49 static PWCHAR
50 CdfsGetNextPathElement(PWCHAR FileName)
51 {
52 if (*FileName == L'\0')
53 {
54 return(NULL);
55 }
56
57 while (*FileName != L'\0' && *FileName != L'\\')
58 {
59 FileName++;
60 }
61
62 return(FileName);
63 }
64
65
66 static VOID
67 CdfsWSubString(PWCHAR pTarget, const PWCHAR pSource, size_t pLength)
68 {
69 wcsncpy (pTarget, pSource, pLength);
70 pTarget [pLength] = L'\0';
71 }
72
73
74 PFCB
75 CdfsCreateFCB(PWSTR FileName)
76 {
77 PFCB Fcb;
78
79 Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), TAG_FCB);
80 RtlZeroMemory(Fcb, sizeof(FCB));
81
82 if (FileName)
83 {
84 wcscpy(Fcb->PathName, FileName);
85 if (wcsrchr(Fcb->PathName, '\\') != 0)
86 {
87 Fcb->ObjectName = wcsrchr(Fcb->PathName, '\\');
88 }
89 else
90 {
91 Fcb->ObjectName = Fcb->PathName;
92 }
93 }
94
95 ExInitializeResourceLite(&Fcb->MainResource);
96
97 return(Fcb);
98 }
99
100
101 VOID
102 CdfsDestroyFCB(PFCB Fcb)
103 {
104 ExDeleteResourceLite(&Fcb->MainResource);
105
106 ExFreePool(Fcb);
107 }
108
109
110 BOOLEAN
111 CdfsFCBIsDirectory(PFCB Fcb)
112 {
113 // return(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY);
114 return(Fcb->Entry.FileFlags & 0x02);
115 }
116
117
118 BOOLEAN
119 CdfsFCBIsRoot(PFCB Fcb)
120 {
121 return(wcscmp(Fcb->PathName, L"\\") == 0);
122 }
123
124
125 VOID
126 CdfsGrabFCB(PDEVICE_EXTENSION Vcb,
127 PFCB Fcb)
128 {
129 KIRQL oldIrql;
130
131 DPRINT("grabbing FCB at %x: %S, refCount:%d\n",
132 Fcb,
133 Fcb->PathName,
134 Fcb->RefCount);
135
136 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
137 Fcb->RefCount++;
138 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
139 }
140
141
142 VOID
143 CdfsReleaseFCB(PDEVICE_EXTENSION Vcb,
144 PFCB Fcb)
145 {
146 KIRQL oldIrql;
147
148 DPRINT("releasing FCB at %x: %S, refCount:%d\n",
149 Fcb,
150 Fcb->PathName,
151 Fcb->RefCount);
152
153 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
154 Fcb->RefCount--;
155 if (Fcb->RefCount <= 0 && !CdfsFCBIsDirectory(Fcb))
156 {
157 RemoveEntryList(&Fcb->FcbListEntry);
158 CcRosReleaseFileCache(NULL, Fcb->RFCB.Bcb);
159 CdfsDestroyFCB(Fcb);
160 }
161 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
162 }
163
164
165 VOID
166 CdfsAddFCBToTable(PDEVICE_EXTENSION Vcb,
167 PFCB Fcb)
168 {
169 KIRQL oldIrql;
170
171 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
172 Fcb->DevExt = Vcb;
173 InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry);
174 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
175 }
176
177
178 PFCB
179 CdfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb,
180 PWSTR FileName)
181 {
182 KIRQL oldIrql;
183 PFCB Fcb;
184 PLIST_ENTRY current_entry;
185
186 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
187
188 if (FileName == NULL || *FileName == 0)
189 {
190 DPRINT("Return FCB for stream file object\n");
191 Fcb = ((PCCB)Vcb->StreamFileObject->FsContext2)->Fcb;
192 Fcb->RefCount++;
193 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
194 return(Fcb);
195 }
196
197 current_entry = Vcb->FcbListHead.Flink;
198 while (current_entry != &Vcb->FcbListHead)
199 {
200 Fcb = CONTAINING_RECORD(current_entry, FCB, FcbListEntry);
201
202 DPRINT("Comparing '%S' and '%S'\n", FileName, Fcb->PathName);
203 if (_wcsicmp(FileName, Fcb->PathName) == 0)
204 {
205 Fcb->RefCount++;
206 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
207 return(Fcb);
208 }
209
210 //FIXME: need to compare against short name in FCB here
211
212 current_entry = current_entry->Flink;
213 }
214 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
215
216 return(NULL);
217 }
218
219
220 NTSTATUS
221 CdfsFCBInitializeCache(PVCB Vcb,
222 PFCB Fcb)
223 {
224 PFILE_OBJECT FileObject;
225 NTSTATUS Status;
226 PCCB newCCB;
227
228 FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice);
229
230 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
231 if (newCCB == NULL)
232 {
233 return(STATUS_INSUFFICIENT_RESOURCES);
234 }
235 RtlZeroMemory(newCCB,
236 sizeof(CCB));
237
238 FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
239 FO_DIRECT_CACHE_PAGING_READ;
240 FileObject->SectionObjectPointers = &Fcb->SectionObjectPointers;
241 FileObject->FsContext = (PVOID) &Fcb->RFCB;
242 FileObject->FsContext2 = newCCB;
243 newCCB->Fcb = Fcb;
244 newCCB->PtrFileObject = FileObject;
245 Fcb->FileObject = FileObject;
246 Fcb->DevExt = Vcb;
247
248 Status = CcRosInitializeFileCache(FileObject,
249 &Fcb->RFCB.Bcb,
250 PAGESIZE);
251 if (!NT_SUCCESS(Status))
252 {
253 DbgPrint("CcRosInitializeFileCache failed\n");
254 KeBugCheck(0);
255 }
256
257 ObDereferenceObject(FileObject);
258 Fcb->Flags |= FCB_CACHE_INITIALIZED;
259
260 return(Status);
261 }
262
263
264 PFCB
265 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb)
266 {
267 PFCB Fcb;
268
269 Fcb = CdfsCreateFCB(L"\\");
270
271 // memset(Fcb->entry.Filename, ' ', 11);
272
273 Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
274 Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
275 Fcb->Entry.FileFlags = 0x02; // FILE_ATTRIBUTE_DIRECTORY;
276 Fcb->RefCount = 1;
277 Fcb->DirIndex = 0;
278 Fcb->RFCB.FileSize.QuadPart = Vcb->CdInfo.RootSize;
279 Fcb->RFCB.ValidDataLength.QuadPart = Vcb->CdInfo.RootSize;
280 Fcb->RFCB.AllocationSize.QuadPart = Vcb->CdInfo.RootSize;
281
282 CdfsFCBInitializeCache(Vcb, Fcb);
283 CdfsAddFCBToTable(Vcb, Fcb);
284 CdfsGrabFCB(Vcb, Fcb);
285
286 return(Fcb);
287 }
288
289
290 PFCB
291 CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb)
292 {
293 PFCB Fcb;
294
295 Fcb = CdfsGrabFCBFromTable(Vcb, L"\\");
296 if (Fcb == NULL)
297 {
298 Fcb = CdfsMakeRootFCB(Vcb);
299 }
300
301 return(Fcb);
302 }
303
304
305 static VOID
306 CdfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt,
307 PDIR_RECORD Record,
308 PWSTR Name)
309 /*
310 * FUNCTION: Retrieves the file name, be it in short or long file name format
311 */
312 {
313 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
314 {
315 wcscpy(Name, L".");
316 }
317 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
318 {
319 wcscpy(Name, L"..");
320 }
321 else
322 {
323 if (DeviceExt->CdInfo.JolietLevel == 0)
324 {
325 ULONG i;
326
327 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
328 Name[i] = (WCHAR)Record->FileId[i];
329 Name[i] = 0;
330 }
331 else
332 {
333 CdfsSwapString(Name, Record->FileId, Record->FileIdLength);
334 }
335 }
336
337 DPRINT("Name '%S'\n", Name);
338 }
339
340
341 NTSTATUS
342 CdfsMakeFCBFromDirEntry(PVCB Vcb,
343 PFCB DirectoryFCB,
344 PWSTR Name,
345 PDIR_RECORD Record,
346 PFCB * fileFCB)
347 {
348 WCHAR pathName[MAX_PATH];
349 PFCB rcFCB;
350 ULONG Size;
351
352 if (Name [0] != 0 && wcslen (DirectoryFCB->PathName) +
353 sizeof(WCHAR) + wcslen (Name) > MAX_PATH)
354 {
355 return(STATUS_OBJECT_NAME_INVALID);
356 }
357
358 wcscpy(pathName, DirectoryFCB->PathName);
359 if (!CdfsFCBIsRoot(DirectoryFCB))
360 {
361 wcscat(pathName, L"\\");
362 }
363
364 if (Name[0] != 0)
365 {
366 wcscat(pathName, Name);
367 }
368 else
369 {
370 WCHAR entryName[MAX_PATH];
371
372 CdfsGetDirEntryName(Vcb, Record, entryName);
373 wcscat(pathName, entryName);
374 }
375
376 rcFCB = CdfsCreateFCB(pathName);
377 memcpy(&rcFCB->Entry, Record, sizeof(DIR_RECORD));
378
379 Size = rcFCB->Entry.DataLengthL;
380
381 rcFCB->RFCB.FileSize.QuadPart = Size;
382 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
383 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, 2096);
384 // DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
385 CdfsFCBInitializeCache(Vcb, rcFCB);
386 rcFCB->RefCount++;
387 CdfsAddFCBToTable(Vcb, rcFCB);
388 *fileFCB = rcFCB;
389
390 return(STATUS_SUCCESS);
391 }
392
393
394 NTSTATUS
395 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb,
396 PFCB Fcb,
397 PFILE_OBJECT FileObject)
398 {
399 NTSTATUS Status;
400 PCCB newCCB;
401
402 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
403 if (newCCB == NULL)
404 {
405 return(STATUS_INSUFFICIENT_RESOURCES);
406 }
407 memset(newCCB, 0, sizeof(CCB));
408
409 FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
410 FO_DIRECT_CACHE_PAGING_READ;
411 FileObject->SectionObjectPointers = &Fcb->SectionObjectPointers;
412 FileObject->FsContext = (PVOID)&Fcb->RFCB;
413 FileObject->FsContext2 = newCCB;
414 newCCB->Fcb = Fcb;
415 newCCB->PtrFileObject = FileObject;
416 Fcb->DevExt = Vcb;
417
418 if (!(Fcb->Flags & FCB_CACHE_INITIALIZED))
419 {
420 Status = CcRosInitializeFileCache(FileObject,
421 &Fcb->RFCB.Bcb,
422 PAGESIZE);
423 if (!NT_SUCCESS(Status))
424 {
425 DbgPrint("CcRosInitializeFileCache failed\n");
426 KeBugCheck(0);
427 }
428 Fcb->Flags |= FCB_CACHE_INITIALIZED;
429 }
430
431 DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
432
433 return(STATUS_SUCCESS);
434 }
435
436
437 NTSTATUS
438 CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt,
439 PFCB DirectoryFcb,
440 PWSTR FileToFind,
441 PFCB *FoundFCB)
442 {
443 WCHAR TempName[2];
444 WCHAR Name[256];
445 PUCHAR Block;
446 ULONG FirstSector;
447 ULONG DirSize;
448 ULONG BufferSize;
449 ULONG SectorCount;
450 PDIR_RECORD Record;
451 ULONG Offset;
452 NTSTATUS Status;
453
454 assert(DeviceExt);
455 assert(DirectoryFcb);
456 assert(FileToFind);
457
458 DPRINT("CdfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
459 DeviceExt,
460 DirectoryFcb,
461 FileToFind);
462 DPRINT("Dir Path:%S\n", DirectoryFcb->PathName);
463
464 /* default to '.' if no filename specified */
465 if (wcslen(FileToFind) == 0)
466 {
467 TempName[0] = L'.';
468 TempName[1] = 0;
469 FileToFind = TempName;
470 }
471
472 FirstSector = DirectoryFcb->Entry.ExtentLocationL;
473 DirSize = DirectoryFcb->Entry.DataLengthL;
474
475 BufferSize = ROUND_UP(DirSize, BLOCKSIZE);
476 SectorCount = BufferSize / BLOCKSIZE;
477
478 DPRINT("FirstSector %lu DirSize %lu BufferSize %lu SectorCount %lu\n",
479 FirstSector, DirSize, BufferSize, SectorCount);
480
481 Block = ExAllocatePool(NonPagedPool, BufferSize);
482
483 Status = CdfsReadSectors(DeviceExt->StorageDevice,
484 FirstSector,
485 SectorCount,
486 Block);
487 if (!NT_SUCCESS(Status))
488 {
489 DPRINT("Reading directory extent failed (Status %lx)\n", Status);
490 ExFreePool(Block);
491 return(Status);
492 }
493
494 Offset = 0;
495 Record = (PDIR_RECORD)Block;
496 while(TRUE)
497 {
498 if (Record->RecordLength == 0)
499 {
500 DPRINT1("RecordLength == 0 Stopped!\n");
501 break;
502 }
503
504 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
505 Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
506
507 CdfsGetDirEntryName(DeviceExt, Record, Name);
508 DPRINT("Name '%S'\n", Name);
509
510 if (wstrcmpjoki(Name, FileToFind))
511 {
512 DPRINT("Match found, %S\n", Name);
513 Status = CdfsMakeFCBFromDirEntry(DeviceExt,
514 DirectoryFcb,
515 Name,
516 Record,
517 FoundFCB);
518
519 ExFreePool(Block);
520
521 return(Status);
522 }
523
524 Offset = Offset + Record->RecordLength;
525 Record = (PDIR_RECORD)(Block + Offset);
526 if (Record->RecordLength == 0)
527 {
528 Offset = ROUND_UP(Offset, 2048);
529 Record = (PDIR_RECORD)(Block + Offset);
530 }
531
532 // if (Offset >= BufferSize)
533 if (Offset >= DirSize)
534 break;
535 }
536
537 ExFreePool(Block);
538
539 return(STATUS_OBJECT_NAME_NOT_FOUND);
540 }
541
542
543 NTSTATUS
544 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb,
545 PFCB *pParentFCB,
546 PFCB *pFCB,
547 const PWSTR pFileName)
548 {
549 NTSTATUS Status;
550 WCHAR pathName [MAX_PATH];
551 WCHAR elementName [MAX_PATH];
552 PWCHAR currentElement;
553 PFCB FCB;
554 PFCB parentFCB;
555
556 DPRINT("CdfsGetFCBForFile(%x, %x, %x, '%S')\n",
557 Vcb,
558 pParentFCB,
559 pFCB,
560 pFileName);
561
562 /* Trivial case, open of the root directory on volume */
563 if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
564 {
565 DPRINT("returning root FCB\n");
566
567 FCB = CdfsOpenRootFCB(Vcb);
568 *pFCB = FCB;
569 *pParentFCB = NULL;
570
571 return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND);
572 }
573 else
574 {
575 currentElement = pFileName + 1;
576 wcscpy (pathName, L"\\");
577 FCB = CdfsOpenRootFCB (Vcb);
578 }
579 parentFCB = NULL;
580
581 /* Parse filename and check each path element for existance and access */
582 while (CdfsGetNextPathElement(currentElement) != 0)
583 {
584 /* Skip blank directory levels */
585 if ((CdfsGetNextPathElement(currentElement) - currentElement) == 0)
586 {
587 currentElement++;
588 continue;
589 }
590
591 DPRINT("Parsing, currentElement:%S\n", currentElement);
592 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB, FCB);
593
594 /* Descend to next directory level */
595 if (parentFCB)
596 {
597 CdfsReleaseFCB(Vcb, parentFCB);
598 parentFCB = NULL;
599 }
600
601 /* fail if element in FCB is not a directory */
602 if (!CdfsFCBIsDirectory(FCB))
603 {
604 DPRINT("Element in requested path is not a directory\n");
605
606 CdfsReleaseFCB(Vcb, FCB);
607 FCB = 0;
608 *pParentFCB = NULL;
609 *pFCB = NULL;
610
611 return(STATUS_OBJECT_PATH_NOT_FOUND);
612 }
613 parentFCB = FCB;
614
615 /* Extract next directory level into dirName */
616 CdfsWSubString(pathName,
617 pFileName,
618 CdfsGetNextPathElement(currentElement) - pFileName);
619 DPRINT(" pathName:%S\n", pathName);
620
621 FCB = CdfsGrabFCBFromTable(Vcb, pathName);
622 if (FCB == NULL)
623 {
624 CdfsWSubString(elementName,
625 currentElement,
626 CdfsGetNextPathElement(currentElement) - currentElement);
627 DPRINT(" elementName:%S\n", elementName);
628
629 Status = CdfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
630 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
631 {
632 *pParentFCB = parentFCB;
633 *pFCB = NULL;
634 currentElement = CdfsGetNextPathElement(currentElement);
635 if (*currentElement == L'\0' || CdfsGetNextPathElement(currentElement + 1) == 0)
636 {
637 return(STATUS_OBJECT_NAME_NOT_FOUND);
638 }
639 else
640 {
641 return(STATUS_OBJECT_PATH_NOT_FOUND);
642 }
643 }
644 else if (!NT_SUCCESS(Status))
645 {
646 CdfsReleaseFCB(Vcb, parentFCB);
647 *pParentFCB = NULL;
648 *pFCB = NULL;
649
650 return(Status);
651 }
652 }
653 currentElement = CdfsGetNextPathElement(currentElement);
654 }
655
656 *pParentFCB = parentFCB;
657 *pFCB = FCB;
658
659 return(STATUS_SUCCESS);
660 }
661
662 /* EOF */