2002-08-08 David Welch <welch@computer2.darkstar.org>
[reactos.git] / reactos / drivers / fs / vfat / fcb.c
1 /* $Id: fcb.c,v 1.16 2002/08/08 17:54:12 dwelch Exp $
2 *
3 *
4 * FILE: fcb.c
5 * PURPOSE: Routines to manipulate FCBs.
6 * COPYRIGHT: See COPYING in the top level directory
7 * PROJECT: ReactOS kernel
8 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
9 * Rex Jolliff (rex@lvcablemodem.com)
10 */
11
12 /* ------------------------------------------------------- INCLUDES */
13
14 #include <ddk/ntddk.h>
15 #include <wchar.h>
16 #include <limits.h>
17
18 #define NDEBUG
19 #include <debug.h>
20
21 #include "vfat.h"
22
23 /* -------------------------------------------------------- DEFINES */
24
25 #define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
26 #define TAG_FCB TAG('V', 'F', 'C', 'B')
27
28 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
29
30 /* -------------------------------------------------------- PUBLICS */
31
32 PVFATFCB
33 vfatNewFCB(PWCHAR pFileName)
34 {
35 PVFATFCB rcFCB;
36
37 rcFCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATFCB), TAG_FCB);
38 memset (rcFCB, 0, sizeof (VFATFCB));
39 if (pFileName)
40 {
41 wcscpy (rcFCB->PathName, pFileName);
42 if (wcsrchr (rcFCB->PathName, '\\') != 0)
43 {
44 rcFCB->ObjectName = wcsrchr (rcFCB->PathName, '\\');
45 }
46 else
47 {
48 rcFCB->ObjectName = rcFCB->PathName;
49 }
50 }
51 ExInitializeResourceLite(&rcFCB->PagingIoResource);
52 ExInitializeResourceLite(&rcFCB->MainResource);
53 return rcFCB;
54 }
55
56 VOID
57 vfatDestroyFCB(PVFATFCB pFCB)
58 {
59 ExDeleteResourceLite(&pFCB->PagingIoResource);
60 ExDeleteResourceLite(&pFCB->MainResource);
61 if ((pFCB->Flags & FCB_IS_PAGE_FILE) && pFCB->FatChainSize)
62 ExFreePool(pFCB->FatChain);
63 ExFreePool (pFCB);
64 }
65
66 BOOL
67 vfatFCBIsDirectory(PDEVICE_EXTENSION pVCB, PVFATFCB FCB)
68 {
69 return FCB->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY;
70 }
71
72 BOOL
73 vfatFCBIsRoot(PVFATFCB FCB)
74 {
75 return wcscmp (FCB->PathName, L"\\") == 0;
76 }
77
78 VOID
79 vfatGrabFCB(PDEVICE_EXTENSION pVCB, PVFATFCB pFCB)
80 {
81 KIRQL oldIrql;
82
83 DPRINT ("grabbing FCB at %x: %S, refCount:%d\n",
84 pFCB,
85 pFCB->PathName,
86 pFCB->RefCount);
87
88 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
89 pFCB->RefCount++;
90 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
91 }
92
93 VOID
94 vfatReleaseFCB(PDEVICE_EXTENSION pVCB, PVFATFCB pFCB)
95 {
96 KIRQL oldIrql;
97
98 DPRINT ("releasing FCB at %x: %S, refCount:%d\n",
99 pFCB,
100 pFCB->PathName,
101 pFCB->RefCount);
102
103 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
104 pFCB->RefCount--;
105 if (pFCB->RefCount <= 0 && (!vfatFCBIsDirectory (pVCB, pFCB) || pFCB->Flags & FCB_DELETE_PENDING))
106 {
107 RemoveEntryList (&pFCB->FcbListEntry);
108 vfatDestroyFCB (pFCB);
109 }
110 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
111 }
112
113 VOID
114 vfatAddFCBToTable(PDEVICE_EXTENSION pVCB, PVFATFCB pFCB)
115 {
116 KIRQL oldIrql;
117
118 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
119 pFCB->pDevExt = pVCB;
120 InsertTailList (&pVCB->FcbListHead, &pFCB->FcbListEntry);
121 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
122 }
123
124 PVFATFCB
125 vfatGrabFCBFromTable(PDEVICE_EXTENSION pVCB, PWSTR pFileName)
126 {
127 KIRQL oldIrql;
128 PVFATFCB rcFCB;
129 PLIST_ENTRY current_entry;
130
131 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
132 current_entry = pVCB->FcbListHead.Flink;
133 while (current_entry != &pVCB->FcbListHead)
134 {
135 rcFCB = CONTAINING_RECORD (current_entry, VFATFCB, FcbListEntry);
136
137 if (wstrcmpi (pFileName, rcFCB->PathName))
138 {
139 rcFCB->RefCount++;
140 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
141 return rcFCB;
142 }
143
144 //FIXME: need to compare against short name in FCB here
145
146 current_entry = current_entry->Flink;
147 }
148 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
149
150 return NULL;
151 }
152
153 NTSTATUS
154 vfatFCBInitializeCache (PVCB vcb, PVFATFCB fcb)
155 {
156 NTSTATUS status;
157 PFILE_OBJECT fileObject;
158 ULONG fileCacheQuantum;
159 PVFATCCB newCCB;
160
161 fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
162
163 newCCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATCCB), TAG_CCB);
164 if (newCCB == NULL)
165 {
166 return STATUS_INSUFFICIENT_RESOURCES;
167 }
168 memset (newCCB, 0, sizeof (VFATCCB));
169
170 fileObject->Flags = fileObject->Flags | FO_FCB_IS_VALID |
171 FO_DIRECT_CACHE_PAGING_READ;
172 fileObject->SectionObjectPointers = &fcb->SectionObjectPointers;
173 fileObject->FsContext = (PVOID) &fcb->RFCB;
174 fileObject->FsContext2 = newCCB;
175 newCCB->pFcb = fcb;
176 newCCB->PtrFileObject = fileObject;
177 fcb->FileObject = fileObject;
178 fcb->pDevExt = vcb;
179
180
181 fileCacheQuantum = (vcb->FatInfo.BytesPerCluster >= PAGESIZE) ?
182 vcb->FatInfo.BytesPerCluster : PAGESIZE;
183
184 status = CcRosInitializeFileCache (fileObject,
185 &fcb->RFCB.Bcb,
186 fileCacheQuantum);
187 if (!NT_SUCCESS (status))
188 {
189 DbgPrint ("CcRosInitializeFileCache failed\n");
190 KeBugCheck (0);
191 }
192
193 ObDereferenceObject (fileObject);
194 fcb->Flags |= FCB_CACHE_INITIALIZED;
195
196 return status;
197 }
198
199 PVFATFCB
200 vfatMakeRootFCB(PDEVICE_EXTENSION pVCB)
201 {
202 PVFATFCB FCB;
203 ULONG FirstCluster, CurrentCluster, Size = 0;
204 NTSTATUS Status = STATUS_SUCCESS;
205
206 FCB = vfatNewFCB(L"\\");
207 memset(FCB->entry.Filename, ' ', 11);
208 FCB->entry.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
209 FCB->entry.Attrib = FILE_ATTRIBUTE_DIRECTORY;
210 if (pVCB->FatInfo.FatType == FAT32)
211 {
212 CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
213 FCB->entry.FirstCluster = FirstCluster & 0xffff;
214 FCB->entry.FirstClusterHigh = FirstCluster >> 16;
215 CurrentCluster = FirstCluster;
216
217 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
218 {
219 Size += pVCB->FatInfo.BytesPerCluster;
220 Status = NextCluster (pVCB, NULL, FirstCluster, &CurrentCluster, FALSE);
221 }
222 }
223 else
224 {
225 FCB->entry.FirstCluster = 1;
226 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
227 }
228 FCB->RefCount = 1;
229 FCB->dirIndex = 0;
230 FCB->RFCB.FileSize.QuadPart = Size;
231 FCB->RFCB.ValidDataLength.QuadPart = Size;
232 FCB->RFCB.AllocationSize.QuadPart = Size;
233
234 vfatFCBInitializeCache(pVCB, FCB);
235 vfatAddFCBToTable(pVCB, FCB);
236 vfatGrabFCB(pVCB, FCB);
237
238 return(FCB);
239 }
240
241 PVFATFCB
242 vfatOpenRootFCB(PDEVICE_EXTENSION pVCB)
243 {
244 PVFATFCB FCB;
245
246 FCB = vfatGrabFCBFromTable (pVCB, L"\\");
247 if (FCB == NULL)
248 {
249 FCB = vfatMakeRootFCB (pVCB);
250 }
251
252 return FCB;
253 }
254
255 NTSTATUS
256 vfatMakeFCBFromDirEntry(PVCB vcb,
257 PVFATFCB directoryFCB,
258 PWSTR longName,
259 PFAT_DIR_ENTRY dirEntry,
260 ULONG dirIndex,
261 PVFATFCB * fileFCB)
262 {
263 PVFATFCB rcFCB;
264 WCHAR pathName [MAX_PATH];
265 ULONG Size;
266 if (longName [0] != 0 && wcslen (directoryFCB->PathName) +
267 sizeof(WCHAR) + wcslen (longName) > MAX_PATH)
268 {
269 return STATUS_OBJECT_NAME_INVALID;
270 }
271 wcscpy (pathName, directoryFCB->PathName);
272 if (!vfatFCBIsRoot (directoryFCB))
273 {
274 wcscat (pathName, L"\\");
275 }
276 if (longName [0] != 0)
277 {
278 wcscat (pathName, longName);
279 }
280 else
281 {
282 WCHAR entryName [MAX_PATH];
283
284 vfatGetDirEntryName (dirEntry, entryName);
285 wcscat (pathName, entryName);
286 }
287 rcFCB = vfatNewFCB (pathName);
288 memcpy (&rcFCB->entry, dirEntry, sizeof (FAT_DIR_ENTRY));
289
290 if (vfatFCBIsDirectory(vcb, rcFCB))
291 {
292 ULONG FirstCluster, CurrentCluster;
293 NTSTATUS Status;
294 Size = 0;
295 FirstCluster = vfatDirEntryGetFirstCluster (vcb, &rcFCB->entry);
296 if (FirstCluster == 1)
297 {
298 Size = vcb->FatInfo.rootDirectorySectors * vcb->FatInfo.BytesPerSector;
299 }
300 else
301 {
302 CurrentCluster = FirstCluster;
303 while (CurrentCluster != 0xffffffff)
304 {
305 Size += vcb->FatInfo.BytesPerCluster;
306 Status = NextCluster (vcb, NULL, FirstCluster, &CurrentCluster, FALSE);
307 }
308 }
309 }
310 else
311 {
312 Size = rcFCB->entry.FileSize;
313 }
314 rcFCB->dirIndex = dirIndex;
315 rcFCB->RFCB.FileSize.QuadPart = Size;
316 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
317 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->FatInfo.BytesPerCluster);
318 // DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
319 vfatFCBInitializeCache (vcb, rcFCB);
320 rcFCB->RefCount++;
321 vfatAddFCBToTable (vcb, rcFCB);
322 *fileFCB = rcFCB;
323
324 return STATUS_SUCCESS;
325 }
326
327 NTSTATUS
328 vfatAttachFCBToFileObject (PDEVICE_EXTENSION vcb,
329 PVFATFCB fcb,
330 PFILE_OBJECT fileObject)
331 {
332 NTSTATUS status;
333 PVFATCCB newCCB;
334
335 newCCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATCCB), TAG_CCB);
336 if (newCCB == NULL)
337 {
338 return STATUS_INSUFFICIENT_RESOURCES;
339 }
340 memset (newCCB, 0, sizeof (VFATCCB));
341
342 fileObject->Flags = fileObject->Flags | FO_FCB_IS_VALID |
343 FO_DIRECT_CACHE_PAGING_READ;
344 fileObject->SectionObjectPointers = &fcb->SectionObjectPointers;
345 fileObject->FsContext = (PVOID) &fcb->RFCB;
346 fileObject->FsContext2 = newCCB;
347 newCCB->pFcb = fcb;
348 newCCB->PtrFileObject = fileObject;
349 fcb->pDevExt = vcb;
350
351 if (!(fcb->Flags & FCB_CACHE_INITIALIZED))
352 {
353 ULONG fileCacheQuantum;
354
355 fileCacheQuantum = (vcb->FatInfo.BytesPerCluster >= PAGESIZE) ?
356 vcb->FatInfo.BytesPerCluster : PAGESIZE;
357 status = CcRosInitializeFileCache (fileObject,
358 &fcb->RFCB.Bcb,
359 fileCacheQuantum);
360 if (!NT_SUCCESS (status))
361 {
362 DbgPrint ("CcRosInitializeFileCache failed\n");
363 KeBugCheck (0);
364 }
365 fcb->Flags |= FCB_CACHE_INITIALIZED;
366 }
367
368 DPRINT ("file open: fcb:%x file size: %d\n", fcb, fcb->entry.FileSize);
369
370 return STATUS_SUCCESS;
371 }
372
373 NTSTATUS
374 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt,
375 PVFATFCB pDirectoryFCB,
376 PWSTR pFileToFind,
377 PVFATFCB * pFoundFCB)
378 {
379 BOOL finishedScanningDirectory;
380 ULONG directoryIndex;
381 NTSTATUS status;
382 WCHAR defaultFileName [2];
383 WCHAR currentLongName [256];
384 FAT_DIR_ENTRY currentDirEntry;
385 WCHAR currentEntryName [256];
386
387 assert (pDeviceExt);
388 assert (pDirectoryFCB);
389 assert (pFileToFind);
390
391 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
392 pDeviceExt,
393 pDirectoryFCB,
394 pFileToFind);
395 DPRINT ("Dir Path:%S\n", pDirectoryFCB->PathName);
396
397 // default to '.' if no filename specified
398 if (wcslen (pFileToFind) == 0)
399 {
400 defaultFileName [0] = L'.';
401 defaultFileName [1] = 0;
402 pFileToFind = defaultFileName;
403 }
404
405 directoryIndex = 0;
406 finishedScanningDirectory = FALSE;
407 while (!finishedScanningDirectory)
408 {
409 status = vfatGetNextDirEntry (pDeviceExt,
410 pDirectoryFCB,
411 &directoryIndex,
412 currentLongName,
413 &currentDirEntry);
414 if (status == STATUS_NO_MORE_ENTRIES)
415 {
416 finishedScanningDirectory = TRUE;
417 continue;
418 }
419 else if (!NT_SUCCESS(status))
420 {
421 return status;
422 }
423
424 DPRINT (" Index:%d longName:%S\n",
425 directoryIndex,
426 currentLongName);
427
428 if (!vfatIsDirEntryDeleted (&currentDirEntry)
429 && !vfatIsDirEntryVolume(&currentDirEntry))
430 {
431 if (currentLongName [0] != L'\0' && wstrcmpjoki (currentLongName, pFileToFind))
432 {
433 DPRINT ("Match found, %S\n", currentLongName);
434 status = vfatMakeFCBFromDirEntry (pDeviceExt,
435 pDirectoryFCB,
436 currentLongName,
437 &currentDirEntry,
438 directoryIndex - 1,
439 pFoundFCB);
440 return status;
441 }
442 else
443 {
444 vfatGetDirEntryName (&currentDirEntry, currentEntryName);
445 DPRINT (" entryName:%S\n", currentEntryName);
446
447 if (wstrcmpjoki (currentEntryName, pFileToFind))
448 {
449 DPRINT ("Match found, %S\n", currentEntryName);
450 status = vfatMakeFCBFromDirEntry (pDeviceExt,
451 pDirectoryFCB,
452 currentLongName,
453 &currentDirEntry,
454 directoryIndex - 1,
455 pFoundFCB);
456 return status;
457 }
458 }
459 }
460 }
461
462 return STATUS_OBJECT_NAME_NOT_FOUND;
463 }
464
465 NTSTATUS
466 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB,
467 PVFATFCB *pParentFCB,
468 PVFATFCB *pFCB,
469 const PWSTR pFileName)
470 {
471 NTSTATUS status;
472 WCHAR pathName [MAX_PATH];
473 WCHAR elementName [MAX_PATH];
474 PWCHAR currentElement;
475 PVFATFCB FCB;
476 PVFATFCB parentFCB;
477
478 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
479 pVCB,
480 pParentFCB,
481 pFCB,
482 pFileName);
483
484 // Trivial case, open of the root directory on volume
485 if (pFileName [0] == L'\0' || wcscmp (pFileName, L"\\") == 0)
486 {
487 DPRINT ("returning root FCB\n");
488
489 FCB = vfatOpenRootFCB (pVCB);
490 *pFCB = FCB;
491 *pParentFCB = NULL;
492
493 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
494 }
495 else
496 {
497 currentElement = pFileName + 1;
498 wcscpy (pathName, L"\\");
499 FCB = vfatOpenRootFCB (pVCB);
500 }
501 parentFCB = NULL;
502
503 // Parse filename and check each path element for existance and access
504 while (vfatGetNextPathElement (currentElement) != 0)
505 {
506 // Skip blank directory levels
507 if ((vfatGetNextPathElement (currentElement) - currentElement) == 0)
508 {
509 currentElement++;
510 continue;
511 }
512
513 DPRINT ("Parsing, currentElement:%S\n", currentElement);
514 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB, FCB);
515
516 // descend to next directory level
517 if (parentFCB)
518 {
519 vfatReleaseFCB (pVCB, parentFCB);
520 parentFCB = 0;
521 }
522 // fail if element in FCB is not a directory
523 if (!vfatFCBIsDirectory (pVCB, FCB))
524 {
525 DPRINT ("Element in requested path is not a directory\n");
526
527 vfatReleaseFCB (pVCB, FCB);
528 FCB = 0;
529 *pParentFCB = NULL;
530 *pFCB = NULL;
531
532 return STATUS_OBJECT_PATH_NOT_FOUND;
533 }
534 parentFCB = FCB;
535
536 // Extract next directory level into dirName
537 vfatWSubString (pathName,
538 pFileName,
539 vfatGetNextPathElement (currentElement) - pFileName);
540 DPRINT (" pathName:%S\n", pathName);
541
542 FCB = vfatGrabFCBFromTable (pVCB, pathName);
543 if (FCB == NULL)
544 {
545 vfatWSubString (elementName,
546 currentElement,
547 vfatGetNextPathElement (currentElement) - currentElement);
548 DPRINT (" elementName:%S\n", elementName);
549
550 status = vfatDirFindFile (pVCB, parentFCB, elementName, &FCB);
551 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
552 {
553 *pParentFCB = parentFCB;
554 *pFCB = NULL;
555 currentElement = vfatGetNextPathElement(currentElement);
556 if (*currentElement == L'\0' || vfatGetNextPathElement(currentElement + 1) == 0)
557 {
558 return STATUS_OBJECT_NAME_NOT_FOUND;
559 }
560 else
561 {
562 return STATUS_OBJECT_PATH_NOT_FOUND;
563 }
564 }
565 else if (!NT_SUCCESS (status))
566 {
567 vfatReleaseFCB (pVCB, parentFCB);
568 *pParentFCB = NULL;
569 *pFCB = NULL;
570
571 return status;
572 }
573 }
574 currentElement = vfatGetNextPathElement (currentElement);
575 }
576
577 *pParentFCB = parentFCB;
578 *pFCB = FCB;
579
580 return STATUS_SUCCESS;
581 }
582
583
584
585