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