e0958200ea152d926065d1bbe6d95d80dc95e3f8
[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.3 2002/04/26 23:21:28 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 PFCB
50 CdfsCreateFCB(PWSTR FileName)
51 {
52 PFCB Fcb;
53
54 Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), TAG_FCB);
55 RtlZeroMemory(Fcb, sizeof(FCB));
56
57 if (FileName)
58 {
59 wcscpy(Fcb->PathName, FileName);
60 if (wcsrchr(Fcb->PathName, '\\') != 0)
61 {
62 Fcb->ObjectName = wcsrchr(Fcb->PathName, '\\');
63 }
64 else
65 {
66 Fcb->ObjectName = Fcb->PathName;
67 }
68 }
69
70 ExInitializeResourceLite(&Fcb->MainResource);
71
72 return(Fcb);
73 }
74
75
76 VOID
77 CdfsDestroyFCB(PFCB Fcb)
78 {
79 ExDeleteResourceLite(&Fcb->MainResource);
80
81 ExFreePool(Fcb);
82 }
83
84
85 BOOLEAN
86 CdfsFCBIsDirectory(PFCB Fcb)
87 {
88 // return(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY);
89 return(Fcb->Entry.FileFlags & 0x02);
90 }
91
92
93 BOOLEAN
94 CdfsFCBIsRoot(PFCB Fcb)
95 {
96 return(wcscmp(Fcb->PathName, L"\\") == 0);
97 }
98
99
100 VOID
101 CdfsGrabFCB(PDEVICE_EXTENSION Vcb,
102 PFCB Fcb)
103 {
104 KIRQL oldIrql;
105
106 DPRINT("grabbing FCB at %x: %S, refCount:%d\n",
107 Fcb,
108 Fcb->PathName,
109 Fcb->RefCount);
110
111 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
112 Fcb->RefCount++;
113 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
114 }
115
116
117 VOID
118 CdfsReleaseFCB(PDEVICE_EXTENSION Vcb,
119 PFCB Fcb)
120 {
121 KIRQL oldIrql;
122
123 DPRINT("releasing FCB at %x: %S, refCount:%d\n",
124 Fcb,
125 Fcb->PathName,
126 Fcb->RefCount);
127
128 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
129 Fcb->RefCount--;
130 if (Fcb->RefCount <= 0 && !CdfsFCBIsDirectory(Fcb))
131 {
132 RemoveEntryList(&Fcb->FcbListEntry);
133 CcRosReleaseFileCache(NULL, Fcb->RFCB.Bcb);
134 CdfsDestroyFCB(Fcb);
135 }
136 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
137 }
138
139
140 VOID
141 CdfsAddFCBToTable(PDEVICE_EXTENSION Vcb,
142 PFCB Fcb)
143 {
144 KIRQL oldIrql;
145
146 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
147 Fcb->DevExt = Vcb;
148 InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry);
149 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
150 }
151
152
153 PFCB
154 CdfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb,
155 PWSTR FileName)
156 {
157 KIRQL oldIrql;
158 PFCB Fcb;
159 PLIST_ENTRY current_entry;
160
161 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
162
163 if (FileName == NULL || *FileName == 0)
164 {
165 DPRINT1("Return FCB for strem file object\n");
166 Fcb = ((PCCB)Vcb->StreamFileObject->FsContext2)->Fcb;
167 Fcb->RefCount++;
168 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
169 return(Fcb);
170 }
171
172 current_entry = Vcb->FcbListHead.Flink;
173 while (current_entry != &Vcb->FcbListHead)
174 {
175 Fcb = CONTAINING_RECORD(current_entry, FCB, FcbListEntry);
176
177 // if (wstrcmpi(FileName, Fcb->PathName))
178 if (_wcsicmp(FileName, Fcb->PathName))
179 {
180 Fcb->RefCount++;
181 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
182 return(Fcb);
183 }
184
185 //FIXME: need to compare against short name in FCB here
186
187 current_entry = current_entry->Flink;
188 }
189 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
190
191 return(NULL);
192 }
193
194
195 NTSTATUS
196 CdfsFCBInitializeCache(PVCB Vcb,
197 PFCB Fcb)
198 {
199 PFILE_OBJECT FileObject;
200 NTSTATUS Status;
201 PCCB newCCB;
202
203 FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice);
204
205 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
206 if (newCCB == NULL)
207 {
208 return(STATUS_INSUFFICIENT_RESOURCES);
209 }
210 RtlZeroMemory(newCCB,
211 sizeof(CCB));
212
213 FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
214 FO_DIRECT_CACHE_PAGING_READ;
215 FileObject->SectionObjectPointers = &Fcb->SectionObjectPointers;
216 FileObject->FsContext = (PVOID) &Fcb->RFCB;
217 FileObject->FsContext2 = newCCB;
218 newCCB->Fcb = Fcb;
219 newCCB->PtrFileObject = FileObject;
220 Fcb->FileObject = FileObject;
221 Fcb->DevExt = Vcb;
222
223 Status = CcRosInitializeFileCache(FileObject,
224 &Fcb->RFCB.Bcb,
225 PAGESIZE);
226 if (!NT_SUCCESS(Status))
227 {
228 DbgPrint("CcRosInitializeFileCache failed\n");
229 KeBugCheck(0);
230 }
231
232 ObDereferenceObject(FileObject);
233 Fcb->Flags |= FCB_CACHE_INITIALIZED;
234
235 return(Status);
236 }
237
238
239 PFCB
240 CdfsMakeRootFCB(PDEVICE_EXTENSION Vcb)
241 {
242 PFCB Fcb;
243
244 Fcb = CdfsCreateFCB(L"\\");
245
246 // memset(Fcb->entry.Filename, ' ', 11);
247
248 Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
249 Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
250 Fcb->Entry.FileFlags = 0x02; // FILE_ATTRIBUTE_DIRECTORY;
251 Fcb->RefCount = 1;
252 Fcb->DirIndex = 0;
253 Fcb->RFCB.FileSize.QuadPart = Vcb->CdInfo.RootSize;
254 Fcb->RFCB.ValidDataLength.QuadPart = Vcb->CdInfo.RootSize;
255 Fcb->RFCB.AllocationSize.QuadPart = Vcb->CdInfo.RootSize;
256
257 CdfsFCBInitializeCache(Vcb, Fcb);
258 CdfsAddFCBToTable(Vcb, Fcb);
259 CdfsGrabFCB(Vcb, Fcb);
260
261 return(Fcb);
262 }
263
264
265 PFCB
266 CdfsOpenRootFCB(PDEVICE_EXTENSION Vcb)
267 {
268 PFCB Fcb;
269
270 Fcb = CdfsGrabFCBFromTable(Vcb, L"\\");
271 if (Fcb == NULL)
272 {
273 Fcb = CdfsMakeRootFCB(Vcb);
274 }
275
276 return(Fcb);
277 }
278
279
280 #if 0
281 NTSTATUS
282 vfatMakeFCBFromDirEntry(PVCB vcb,
283 PVFATFCB directoryFCB,
284 PWSTR longName,
285 PFAT_DIR_ENTRY dirEntry,
286 ULONG dirIndex,
287 PVFATFCB * fileFCB)
288 {
289 PVFATFCB rcFCB;
290 WCHAR pathName [MAX_PATH];
291 ULONG Size;
292 if (longName [0] != 0 && wcslen (directoryFCB->PathName) +
293 sizeof(WCHAR) + wcslen (longName) > MAX_PATH)
294 {
295 return STATUS_OBJECT_NAME_INVALID;
296 }
297 wcscpy (pathName, directoryFCB->PathName);
298 if (!vfatFCBIsRoot (directoryFCB))
299 {
300 wcscat (pathName, L"\\");
301 }
302 if (longName [0] != 0)
303 {
304 wcscat (pathName, longName);
305 }
306 else
307 {
308 WCHAR entryName [MAX_PATH];
309
310 vfatGetDirEntryName (dirEntry, entryName);
311 wcscat (pathName, entryName);
312 }
313 rcFCB = vfatNewFCB (pathName);
314 memcpy (&rcFCB->entry, dirEntry, sizeof (FAT_DIR_ENTRY));
315
316 if (vfatFCBIsDirectory(vcb, rcFCB))
317 {
318 ULONG FirstCluster, CurrentCluster;
319 NTSTATUS Status;
320 Size = 0;
321 FirstCluster = vfatDirEntryGetFirstCluster (vcb, &rcFCB->entry);
322 if (FirstCluster == 1)
323 {
324 Size = vcb->FatInfo.rootDirectorySectors * BLOCKSIZE;
325 }
326 else
327 {
328 CurrentCluster = FirstCluster;
329 while (CurrentCluster != 0xffffffff)
330 {
331 Size += vcb->FatInfo.BytesPerCluster;
332 Status = NextCluster (vcb, NULL, FirstCluster, &CurrentCluster, FALSE);
333 }
334 }
335 }
336 else
337 {
338 Size = rcFCB->entry.FileSize;
339 }
340 rcFCB->dirIndex = dirIndex;
341 rcFCB->RFCB.FileSize.QuadPart = Size;
342 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
343 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->FatInfo.BytesPerCluster);
344 // DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
345 vfatFCBInitializeCache (vcb, rcFCB);
346 rcFCB->RefCount++;
347 vfatAddFCBToTable (vcb, rcFCB);
348 *fileFCB = rcFCB;
349
350 return STATUS_SUCCESS;
351 }
352 #endif
353
354 NTSTATUS
355 CdfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb,
356 PFCB Fcb,
357 PFILE_OBJECT FileObject)
358 {
359 NTSTATUS Status;
360 PCCB newCCB;
361
362 newCCB = ExAllocatePoolWithTag (NonPagedPool, sizeof(CCB), TAG_CCB);
363 if (newCCB == NULL)
364 {
365 return(STATUS_INSUFFICIENT_RESOURCES);
366 }
367 memset(newCCB, 0, sizeof(CCB));
368
369 FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
370 FO_DIRECT_CACHE_PAGING_READ;
371 FileObject->SectionObjectPointers = &Fcb->SectionObjectPointers;
372 FileObject->FsContext = (PVOID)&Fcb->RFCB;
373 FileObject->FsContext2 = newCCB;
374 newCCB->Fcb = Fcb;
375 newCCB->PtrFileObject = FileObject;
376 Fcb->DevExt = Vcb;
377
378 if (!(Fcb->Flags & FCB_CACHE_INITIALIZED))
379 {
380 Status = CcRosInitializeFileCache(FileObject,
381 &Fcb->RFCB.Bcb,
382 PAGESIZE);
383 if (!NT_SUCCESS(Status))
384 {
385 DbgPrint("CcRosInitializeFileCache failed\n");
386 KeBugCheck(0);
387 }
388 Fcb->Flags |= FCB_CACHE_INITIALIZED;
389 }
390
391 DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
392
393 return(STATUS_SUCCESS);
394 }
395
396 #if 0
397 NTSTATUS
398 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt,
399 PVFATFCB pDirectoryFCB,
400 PWSTR pFileToFind,
401 PVFATFCB * pFoundFCB)
402 {
403 BOOL finishedScanningDirectory;
404 ULONG directoryIndex;
405 NTSTATUS status;
406 WCHAR defaultFileName [2];
407 WCHAR currentLongName [256];
408 FAT_DIR_ENTRY currentDirEntry;
409 WCHAR currentEntryName [256];
410
411 assert (pDeviceExt);
412 assert (pDirectoryFCB);
413 assert (pFileToFind);
414
415 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
416 pDeviceExt,
417 pDirectoryFCB,
418 pFileToFind);
419 DPRINT ("Dir Path:%S\n", pDirectoryFCB->PathName);
420
421 // default to '.' if no filename specified
422 if (wcslen (pFileToFind) == 0)
423 {
424 defaultFileName [0] = L'.';
425 defaultFileName [1] = 0;
426 pFileToFind = defaultFileName;
427 }
428
429 directoryIndex = 0;
430 finishedScanningDirectory = FALSE;
431 while (!finishedScanningDirectory)
432 {
433 status = vfatGetNextDirEntry (pDeviceExt,
434 pDirectoryFCB,
435 &directoryIndex,
436 currentLongName,
437 &currentDirEntry);
438 if (status == STATUS_NO_MORE_ENTRIES)
439 {
440 finishedScanningDirectory = TRUE;
441 continue;
442 }
443 else if (!NT_SUCCESS(status))
444 {
445 return status;
446 }
447
448 DPRINT (" Index:%d longName:%S\n",
449 directoryIndex,
450 currentLongName);
451
452 if (!vfatIsDirEntryDeleted (&currentDirEntry)
453 && !vfatIsDirEntryVolume(&currentDirEntry))
454 {
455 if (currentLongName [0] != L'\0' && wstrcmpjoki (currentLongName, pFileToFind))
456 {
457 DPRINT ("Match found, %S\n", currentLongName);
458 status = vfatMakeFCBFromDirEntry (pDeviceExt,
459 pDirectoryFCB,
460 currentLongName,
461 &currentDirEntry,
462 directoryIndex - 1,
463 pFoundFCB);
464 return status;
465 }
466 else
467 {
468 vfatGetDirEntryName (&currentDirEntry, currentEntryName);
469 DPRINT (" entryName:%S\n", currentEntryName);
470
471 if (wstrcmpjoki (currentEntryName, pFileToFind))
472 {
473 DPRINT ("Match found, %S\n", currentEntryName);
474 status = vfatMakeFCBFromDirEntry (pDeviceExt,
475 pDirectoryFCB,
476 currentLongName,
477 &currentDirEntry,
478 directoryIndex - 1,
479 pFoundFCB);
480 return status;
481 }
482 }
483 }
484 }
485
486 return STATUS_OBJECT_NAME_NOT_FOUND;
487 }
488 #endif
489
490
491 NTSTATUS
492 CdfsGetFCBForFile(PDEVICE_EXTENSION Vcb,
493 PFCB *pParentFCB,
494 PFCB *pFCB,
495 const PWSTR pFileName)
496 {
497 NTSTATUS status;
498 WCHAR pathName [MAX_PATH];
499 WCHAR elementName [MAX_PATH];
500 PWCHAR currentElement;
501 PFCB FCB;
502 PFCB parentFCB;
503
504 DPRINT("CdfsGetFCBForFile(%x, %x, %x, '%S')\n",
505 Vcb,
506 pParentFCB,
507 pFCB,
508 pFileName);
509
510 /* Trivial case, open of the root directory on volume */
511 if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
512 {
513 DPRINT("returning root FCB\n");
514
515 FCB = CdfsOpenRootFCB(Vcb);
516 *pFCB = FCB;
517 *pParentFCB = NULL;
518
519 return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND);
520 }
521
522 DPRINT1("CdfsGetFCBForFile() is incomplete!\n");
523 return(STATUS_UNSUCCESSFUL);
524
525 #if 0
526 else
527 {
528 currentElement = pFileName + 1;
529 wcscpy (pathName, L"\\");
530 FCB = CdfsOpenRootFCB (Vcb);
531 }
532 parentFCB = NULL;
533
534 // Parse filename and check each path element for existance and access
535 while (vfatGetNextPathElement (currentElement) != 0)
536 {
537 // Skip blank directory levels
538 if ((vfatGetNextPathElement (currentElement) - currentElement) == 0)
539 {
540 currentElement++;
541 continue;
542 }
543
544 DPRINT ("Parsing, currentElement:%S\n", currentElement);
545 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB, FCB);
546
547 // descend to next directory level
548 if (parentFCB)
549 {
550 vfatReleaseFCB (pVCB, parentFCB);
551 parentFCB = 0;
552 }
553 // fail if element in FCB is not a directory
554 if (!vfatFCBIsDirectory (pVCB, FCB))
555 {
556 DPRINT ("Element in requested path is not a directory\n");
557
558 vfatReleaseFCB (pVCB, FCB);
559 FCB = 0;
560 *pParentFCB = NULL;
561 *pFCB = NULL;
562
563 return STATUS_OBJECT_PATH_NOT_FOUND;
564 }
565 parentFCB = FCB;
566
567 // Extract next directory level into dirName
568 vfatWSubString (pathName,
569 pFileName,
570 vfatGetNextPathElement (currentElement) - pFileName);
571 DPRINT (" pathName:%S\n", pathName);
572
573 FCB = vfatGrabFCBFromTable (pVCB, pathName);
574 if (FCB == NULL)
575 {
576 vfatWSubString (elementName,
577 currentElement,
578 vfatGetNextPathElement (currentElement) - currentElement);
579 DPRINT (" elementName:%S\n", elementName);
580
581 status = vfatDirFindFile (pVCB, parentFCB, elementName, &FCB);
582 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
583 {
584 *pParentFCB = parentFCB;
585 *pFCB = NULL;
586 currentElement = vfatGetNextPathElement(currentElement);
587 if (*currentElement == L'\0' || vfatGetNextPathElement(currentElement + 1) == 0)
588 {
589 return STATUS_OBJECT_NAME_NOT_FOUND;
590 }
591 else
592 {
593 return STATUS_OBJECT_PATH_NOT_FOUND;
594 }
595 }
596 else if (!NT_SUCCESS (status))
597 {
598 vfatReleaseFCB (pVCB, parentFCB);
599 *pParentFCB = NULL;
600 *pFCB = NULL;
601
602 return status;
603 }
604 }
605 currentElement = vfatGetNextPathElement (currentElement);
606 }
607
608 *pParentFCB = parentFCB;
609 *pFCB = FCB;
610
611 return STATUS_SUCCESS;
612 #endif
613 }
614
615 /* EOF */