Fixed FCB management functions.
[reactos.git] / reactos / drivers / fs / cdfs / dirctl.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: dirctl.c,v 1.3 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/dirctl.c
24 * PURPOSE: CDROM (ISO 9660) filesystem driver
25 * PROGRAMMER: Art Yerkes
26 * Eric Kohl
27 * UPDATE HISTORY:
28 */
29
30 /* INCLUDES *****************************************************************/
31
32 #include <ddk/ntddk.h>
33
34 #define NDEBUG
35 #include <debug.h>
36
37 #include "cdfs.h"
38
39
40 /* FUNCTIONS ****************************************************************/
41
42 static NTSTATUS
43 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt,
44 PVOID Block,
45 ULONG BlockLength,
46 PVOID *Ptr,
47 PWSTR Name,
48 PULONG pIndex,
49 PULONG pIndex2)
50 /*
51 * FUNCTION: Retrieves the file name, be it in short or long file name format
52 */
53 {
54 PDIR_RECORD Record;
55 NTSTATUS Status;
56 ULONG Index = 0;
57 ULONG Offset = 0;
58
59 Record = (PDIR_RECORD)Block;
60 while(Index < *pIndex)
61 {
62 Offset = Offset + Record->RecordLength;
63
64 Record = (PDIR_RECORD)(Block + Offset);
65 if (Record->RecordLength == 0)
66 {
67 Offset = ROUND_UP(Offset, 2048);
68 Record = (PDIR_RECORD)(Block + Offset);
69 }
70
71 if (Offset >= BlockLength)
72 return(STATUS_NO_MORE_ENTRIES);
73
74 Index++;
75 }
76
77 DPRINT("Index %lu RecordLength %lu Offset %lu\n",
78 Index, Record->RecordLength, Offset);
79
80 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
81 {
82 wcscpy(Name, L".");
83 }
84 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
85 {
86 wcscpy(Name, L"..");
87 }
88 else
89 {
90 if (DeviceExt->CdInfo.JolietLevel == 0)
91 {
92 ULONG i;
93
94 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
95 Name[i] = (WCHAR)Record->FileId[i];
96 Name[i] = 0;
97 }
98 else
99 {
100 CdfsSwapString(Name, Record->FileId, Record->FileIdLength);
101 }
102 }
103
104 DPRINT("Name '%S'\n", Name);
105
106 *Ptr = Record;
107
108 *pIndex = Index;
109
110 return(STATUS_SUCCESS);
111 }
112
113
114 static NTSTATUS
115 CdfsFindFile(PDEVICE_EXTENSION DeviceExt,
116 PFCB Fcb,
117 PFCB Parent,
118 PWSTR FileToFind,
119 PULONG pDirIndex,
120 PULONG pDirIndex2)
121 /*
122 * FUNCTION: Find a file
123 */
124 {
125 WCHAR name[256];
126 char * block;
127 WCHAR TempStr[2];
128 NTSTATUS Status;
129 ULONG len;
130 ULONG DirIndex;
131 ULONG Offset;
132 ULONG FirstSector;
133 ULONG Read;
134 BOOLEAN IsRoot;
135 LARGE_INTEGER FileOffset;
136 PVOID Context = NULL;
137
138 ULONG DirSize;
139 ULONG BufferSize;
140 ULONG SectorCount;
141 PUCHAR Ptr;
142 PDIR_RECORD Record;
143
144 DPRINT("FindFile(Parent %x, FileToFind '%S', DirIndex: %d)\n",
145 Parent, FileToFind, pDirIndex ? *pDirIndex : 0);
146 DPRINT("FindFile: old Pathname %x, old Objectname %x)\n",
147 Fcb->PathName, Fcb->ObjectName);
148
149 IsRoot = FALSE;
150 DirIndex = 0;
151 if (wcslen (FileToFind) == 0)
152 {
153 CHECKPOINT;
154 TempStr[0] = (WCHAR) '.';
155 TempStr[1] = 0;
156 FileToFind = (PWSTR)&TempStr;
157 }
158
159 if (Parent)
160 {
161 FirstSector = Parent->Entry.ExtentLocationL;
162 if (FirstSector == DeviceExt->CdInfo.RootStart)
163 {
164 IsRoot = TRUE;
165 }
166 }
167 else
168 {
169 IsRoot = TRUE;
170 }
171
172 if (IsRoot == TRUE)
173 {
174 FirstSector = DeviceExt->CdInfo.RootStart;
175 DirSize = DeviceExt->CdInfo.RootSize;
176
177
178 if (FileToFind[0] == 0 || (FileToFind[0] == '\\' && FileToFind[1] == 0)
179 || (FileToFind[0] == '.' && FileToFind[1] == 0))
180 {
181 /* it's root : complete essentials fields then return ok */
182 RtlZeroMemory(Fcb, sizeof(FCB));
183
184 Fcb->PathName[0]='\\';
185 Fcb->ObjectName = &Fcb->PathName[1];
186 Fcb->Entry.ExtentLocationL = DeviceExt->CdInfo.RootStart;
187 Fcb->Entry.DataLengthL = DeviceExt->CdInfo.RootSize;
188 Fcb->Entry.FileFlags = 0x02; //FILE_ATTRIBUTE_DIRECTORY;
189
190 if (pDirIndex)
191 *pDirIndex = 0;
192 if (pDirIndex2)
193 *pDirIndex2 = 0;
194 DPRINT("CdfsFindFile: new Pathname %S, new Objectname %S)\n",Fcb->PathName, Fcb->ObjectName);
195 return (STATUS_SUCCESS);
196 }
197 }
198 else
199 {
200 FirstSector = Parent->Entry.ExtentLocationL;
201 DirSize = Parent->Entry.DataLengthL;
202 }
203
204 DPRINT("FirstSector %lu DirSize %lu\n", FirstSector, DirSize);
205
206 if (pDirIndex && (*pDirIndex))
207 DirIndex = *pDirIndex;
208
209 BufferSize = ROUND_UP(DirSize, BLOCKSIZE);
210 SectorCount = BufferSize / BLOCKSIZE;
211
212 DPRINT("FirstSector %lu DirSize %lu BufferSize %lu SectorCount %lu\n",
213 FirstSector, DirSize, BufferSize, SectorCount);
214
215 block = ExAllocatePool(NonPagedPool, BufferSize);
216
217 Status = CdfsReadSectors(DeviceExt->StorageDevice,
218 FirstSector,
219 SectorCount,
220 block);
221 if (!NT_SUCCESS(Status))
222 {
223 DPRINT("Reading directory extent failed (Status %lx)\n", Status);
224 ExFreePool(block);
225 return(Status);
226 }
227
228 Ptr = (PUCHAR)block;
229 while(TRUE)
230 {
231 Record = (PDIR_RECORD)Ptr;
232 if (Record->RecordLength == 0)
233 {
234 DPRINT1("Stopped!\n");
235 break;
236 }
237
238 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
239 Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
240
241 Status = CdfsGetEntryName(DeviceExt, block, DirSize, (PVOID*)&Ptr, name, &DirIndex, pDirIndex2);
242 if (Status == STATUS_NO_MORE_ENTRIES)
243 {
244 break;
245 }
246
247 DPRINT("Name '%S'\n", name);
248
249 if (wstrcmpjoki(name, FileToFind)) /* || wstrcmpjoki (name2, FileToFind)) */
250 {
251 if (Parent && Parent->PathName)
252 {
253 len = wcslen(Parent->PathName);
254 memcpy(Fcb->PathName, Parent->PathName, len*sizeof(WCHAR));
255 Fcb->ObjectName=&Fcb->PathName[len];
256 if (len != 1 || Fcb->PathName[0] != '\\')
257 {
258 Fcb->ObjectName[0] = '\\';
259 Fcb->ObjectName = &Fcb->ObjectName[1];
260 }
261 }
262 else
263 {
264 Fcb->ObjectName=Fcb->PathName;
265 Fcb->ObjectName[0]='\\';
266 Fcb->ObjectName=&Fcb->ObjectName[1];
267 }
268
269 DPRINT("PathName '%S' ObjectName '%S'\n", Fcb->PathName, Fcb->ObjectName);
270
271 memcpy(&Fcb->Entry, Ptr, sizeof(DIR_RECORD));
272 wcsncpy(Fcb->ObjectName, name, MAX_PATH);
273 if (pDirIndex)
274 *pDirIndex = DirIndex;
275
276 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",
277 Fcb->PathName, Fcb->ObjectName, DirIndex);
278
279 ExFreePool(block);
280
281 return(STATUS_SUCCESS);
282 }
283
284
285 Ptr = Ptr + Record->RecordLength;
286 DirIndex++;
287
288 if (((ULONG)Ptr - (ULONG)block) >= DirSize)
289 {
290 DPRINT("Stopped!\n");
291 break;
292 }
293 }
294
295 ExFreePool(block);
296
297 if (pDirIndex)
298 *pDirIndex = DirIndex;
299
300 return(STATUS_UNSUCCESSFUL);
301 }
302
303
304 static NTSTATUS
305 CdfsGetNameInformation(PFCB Fcb,
306 PDEVICE_EXTENSION DeviceExt,
307 PFILE_NAMES_INFORMATION Info,
308 ULONG BufferLength)
309 {
310 ULONG Length;
311
312 DPRINT("CdfsGetNameInformation() called\n");
313
314 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
315 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
316 return(STATUS_BUFFER_OVERFLOW);
317
318 Info->FileNameLength = Length;
319 Info->NextEntryOffset =
320 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
321 memcpy(Info->FileName, Fcb->ObjectName, Length);
322
323 return(STATUS_SUCCESS);
324 }
325
326
327 static NTSTATUS
328 CdfsGetDirectoryInformation(PFCB Fcb,
329 PDEVICE_EXTENSION DeviceExt,
330 PFILE_DIRECTORY_INFORMATION Info,
331 ULONG BufferLength)
332 {
333 ULONG Length;
334
335 DPRINT1("CdfsGetDirectoryInformation() called\n");
336
337 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
338 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
339 return(STATUS_BUFFER_OVERFLOW);
340
341 Info->FileNameLength = Length;
342 Info->NextEntryOffset =
343 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
344 memcpy(Info->FileName, Fcb->ObjectName, Length);
345
346 /* Convert file times */
347 CdfsDateTimeToFileTime(Fcb,
348 &Info->CreationTime);
349 CdfsDateTimeToFileTime(Fcb,
350 &Info->LastAccessTime);
351 CdfsDateTimeToFileTime(Fcb,
352 &Info->LastWriteTime);
353 CdfsDateTimeToFileTime(Fcb,
354 &Info->ChangeTime);
355
356 /* Convert file flags */
357 CdfsFileFlagsToAttributes(Fcb,
358 &Info->FileAttributes);
359
360 Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
361
362 /* Make AllocSize a rounded up multiple of the sector size */
363 Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
364
365 // Info->FileIndex=;
366
367 return(STATUS_SUCCESS);
368 }
369
370
371 static NTSTATUS
372 CdfsGetFullDirectoryInformation(PFCB Fcb,
373 PDEVICE_EXTENSION DeviceExt,
374 PFILE_FULL_DIRECTORY_INFORMATION Info,
375 ULONG BufferLength)
376 {
377 ULONG Length;
378
379 DPRINT1("CdfsGetFullDirectoryInformation() called\n");
380
381 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
382 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
383 return(STATUS_BUFFER_OVERFLOW);
384
385 Info->FileNameLength = Length;
386 Info->NextEntryOffset =
387 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
388 memcpy(Info->FileName, Fcb->ObjectName, Length);
389
390 /* Convert file times */
391 CdfsDateTimeToFileTime(Fcb,
392 &Info->CreationTime);
393 CdfsDateTimeToFileTime(Fcb,
394 &Info->LastAccessTime);
395 CdfsDateTimeToFileTime(Fcb,
396 &Info->LastWriteTime);
397 CdfsDateTimeToFileTime(Fcb,
398 &Info->ChangeTime);
399
400 /* Convert file flags */
401 CdfsFileFlagsToAttributes(Fcb,
402 &Info->FileAttributes);
403
404 Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
405
406 /* Make AllocSize a rounded up multiple of the sector size */
407 Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
408
409 // Info->FileIndex=;
410 Info->EaSize = 0;
411
412 return(STATUS_SUCCESS);
413 }
414
415
416 static NTSTATUS
417 CdfsGetBothDirectoryInformation(PFCB Fcb,
418 PDEVICE_EXTENSION DeviceExt,
419 PFILE_BOTH_DIRECTORY_INFORMATION Info,
420 ULONG BufferLength)
421 {
422 ULONG Length;
423
424 DPRINT("CdfsGetBothDirectoryInformation() called\n");
425
426 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
427 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
428 return(STATUS_BUFFER_OVERFLOW);
429
430 Info->FileNameLength = Length;
431 Info->NextEntryOffset =
432 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
433 memcpy(Info->FileName, Fcb->ObjectName, Length);
434
435 /* Convert file times */
436 CdfsDateTimeToFileTime(Fcb,
437 &Info->CreationTime);
438 CdfsDateTimeToFileTime(Fcb,
439 &Info->LastAccessTime);
440 CdfsDateTimeToFileTime(Fcb,
441 &Info->LastWriteTime);
442 CdfsDateTimeToFileTime(Fcb,
443 &Info->ChangeTime);
444
445 /* Convert file flags */
446 CdfsFileFlagsToAttributes(Fcb,
447 &Info->FileAttributes);
448
449 Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
450
451 /* Make AllocSize a rounded up multiple of the sector size */
452 Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
453
454 // Info->FileIndex=;
455 Info->EaSize = 0;
456
457 if (DeviceExt->CdInfo.JolietLevel == 0)
458 {
459 /* Standard ISO-9660 format */
460 Info->ShortNameLength = Length;
461 memcpy(Info->ShortName, Fcb->ObjectName, Length);
462 }
463 else
464 {
465 /* Joliet extension */
466
467 /* FIXME: Copy or create a short file name */
468
469 Info->ShortName[0] = 0;
470 Info->ShortNameLength = 0;
471 }
472
473 return(STATUS_SUCCESS);
474 }
475
476
477 static NTSTATUS
478 CdfsQueryDirectory(PDEVICE_OBJECT DeviceObject,
479 PIRP Irp)
480 {
481 PDEVICE_EXTENSION DeviceExtension;
482 LONG BufferLength = 0;
483 PUNICODE_STRING SearchPattern = NULL;
484 FILE_INFORMATION_CLASS FileInformationClass;
485 ULONG FileIndex = 0;
486 PUCHAR Buffer = NULL;
487 PFILE_NAMES_INFORMATION Buffer0 = NULL;
488 PFCB Fcb;
489 PCCB Ccb;
490 FCB TempFcb;
491 BOOLEAN First = FALSE;
492 PIO_STACK_LOCATION Stack;
493 PFILE_OBJECT FileObject;
494 NTSTATUS Status = STATUS_SUCCESS;
495
496 DPRINT("CdfsQueryDirectory() called\n");
497
498 DeviceExtension = DeviceObject->DeviceExtension;
499 Stack = IoGetCurrentIrpStackLocation(Irp);
500 FileObject = Stack->FileObject;
501
502 Ccb = (PCCB)FileObject->FsContext2;
503 Fcb = Ccb->Fcb;
504
505 /* Obtain the callers parameters */
506 BufferLength = Stack->Parameters.QueryDirectory.Length;
507 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
508 FileInformationClass =
509 Stack->Parameters.QueryDirectory.FileInformationClass;
510 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
511
512
513 if (SearchPattern != NULL)
514 {
515 if (!Ccb->DirectorySearchPattern)
516 {
517 First = TRUE;
518 Ccb->DirectorySearchPattern =
519 ExAllocatePool(NonPagedPool, SearchPattern->Length + sizeof(WCHAR));
520 if (!Ccb->DirectorySearchPattern)
521 {
522 return(STATUS_INSUFFICIENT_RESOURCES);
523 }
524
525 memcpy(Ccb->DirectorySearchPattern,
526 SearchPattern->Buffer,
527 SearchPattern->Length);
528 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
529 }
530 }
531 else if (!Ccb->DirectorySearchPattern)
532 {
533 First = TRUE;
534 Ccb->DirectorySearchPattern = ExAllocatePool(NonPagedPool, 2 * sizeof(WCHAR));
535 if (!Ccb->DirectorySearchPattern)
536 {
537 return(STATUS_INSUFFICIENT_RESOURCES);
538 }
539 Ccb->DirectorySearchPattern[0] = L'*';
540 Ccb->DirectorySearchPattern[1] = 0;
541 }
542 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
543
544 /* Determine directory index */
545 if (Stack->Flags & SL_INDEX_SPECIFIED)
546 {
547 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
548 }
549 else if (First || (Stack->Flags & SL_RESTART_SCAN))
550 {
551 Ccb->Entry = 0;
552 }
553
554 /* Determine Buffer for result */
555 if (Irp->MdlAddress)
556 {
557 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
558 }
559 else
560 {
561 Buffer = Irp->UserBuffer;
562 }
563 DPRINT("Buffer=%x tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
564
565 TempFcb.ObjectName = TempFcb.PathName;
566 while (Status == STATUS_SUCCESS && BufferLength > 0)
567 {
568 Status = CdfsFindFile(DeviceExtension,
569 &TempFcb,
570 Fcb,
571 Ccb->DirectorySearchPattern,
572 &Ccb->Entry,
573 NULL);
574 DPRINT("Found %S, Status=%x, entry %x\n", TempFcb.ObjectName, Status, Ccb->Entry);
575
576 if (NT_SUCCESS(Status))
577 {
578 switch (FileInformationClass)
579 {
580 case FileNameInformation:
581 Status = CdfsGetNameInformation(&TempFcb,
582 DeviceExtension,
583 (PFILE_NAMES_INFORMATION)Buffer,
584 BufferLength);
585 break;
586
587 case FileDirectoryInformation:
588 Status = CdfsGetDirectoryInformation(&TempFcb,
589 DeviceExtension,
590 (PFILE_DIRECTORY_INFORMATION)Buffer,
591 BufferLength);
592 break;
593
594 case FileFullDirectoryInformation:
595 Status = CdfsGetFullDirectoryInformation(&TempFcb,
596 DeviceExtension,
597 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
598 BufferLength);
599 break;
600
601 case FileBothDirectoryInformation:
602 Status = CdfsGetBothDirectoryInformation(&TempFcb,
603 DeviceExtension,
604 (PFILE_BOTH_DIRECTORY_INFORMATION)Buffer,
605 BufferLength);
606 break;
607
608 default:
609 Status = STATUS_INVALID_INFO_CLASS;
610 }
611
612 if (Status == STATUS_BUFFER_OVERFLOW)
613 {
614 if (Buffer0)
615 {
616 Buffer0->NextEntryOffset = 0;
617 }
618 break;
619 }
620 }
621 else
622 {
623 if (Buffer0)
624 {
625 Buffer0->NextEntryOffset = 0;
626 }
627
628 if (First)
629 {
630 Status = STATUS_NO_SUCH_FILE;
631 }
632 else
633 {
634 Status = STATUS_NO_MORE_FILES;
635 }
636 break;
637 }
638
639 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
640 Buffer0->FileIndex = FileIndex++;
641 Ccb->Entry++;
642
643 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
644 {
645 break;
646 }
647 BufferLength -= Buffer0->NextEntryOffset;
648 Buffer += Buffer0->NextEntryOffset;
649 }
650
651 if (Buffer0)
652 {
653 Buffer0->NextEntryOffset = 0;
654 }
655
656 if (FileIndex > 0)
657 {
658 Status = STATUS_SUCCESS;
659 }
660
661 return(Status);
662 }
663
664
665
666 NTSTATUS STDCALL
667 CdfsDirectoryControl(PDEVICE_OBJECT DeviceObject,
668 PIRP Irp)
669 {
670 PIO_STACK_LOCATION Stack;
671 NTSTATUS Status;
672
673 DPRINT("CdfsDirectoryControl() called\n");
674
675 Stack = IoGetCurrentIrpStackLocation(Irp);
676
677 switch (Stack->MinorFunction)
678 {
679 case IRP_MN_QUERY_DIRECTORY:
680 Status = CdfsQueryDirectory(DeviceObject,
681 Irp);
682 break;
683
684 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
685 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
686 Status = STATUS_NOT_IMPLEMENTED;
687 break;
688
689 default:
690 DPRINT1("CDFS: MinorFunction %d\n", Stack->MinorFunction);
691 Status = STATUS_INVALID_DEVICE_REQUEST;
692 break;
693 }
694
695 Irp->IoStatus.Status = Status;
696 Irp->IoStatus.Information = 0;
697
698 IoCompleteRequest(Irp, IO_NO_INCREMENT);
699
700 return(Status);
701 }
702
703 /* EOF */