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