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