All:
[reactos.git] / reactos / drivers / fs / vfat / fcb.c
1 /* $Id: fcb.c,v 1.14 2002/03/18 22:37:12 hbirr 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 CcRosReleaseFileCache (NULL, pFCB->RFCB.Bcb);
109 vfatDestroyFCB (pFCB);
110 }
111 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
112 }
113
114 VOID
115 vfatAddFCBToTable(PDEVICE_EXTENSION pVCB, PVFATFCB pFCB)
116 {
117 KIRQL oldIrql;
118
119 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
120 pFCB->pDevExt = pVCB;
121 InsertTailList (&pVCB->FcbListHead, &pFCB->FcbListEntry);
122 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
123 }
124
125 PVFATFCB
126 vfatGrabFCBFromTable(PDEVICE_EXTENSION pVCB, PWSTR pFileName)
127 {
128 KIRQL oldIrql;
129 PVFATFCB rcFCB;
130 PLIST_ENTRY current_entry;
131
132 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
133 current_entry = pVCB->FcbListHead.Flink;
134 while (current_entry != &pVCB->FcbListHead)
135 {
136 rcFCB = CONTAINING_RECORD (current_entry, VFATFCB, FcbListEntry);
137
138 if (wstrcmpi (pFileName, rcFCB->PathName))
139 {
140 rcFCB->RefCount++;
141 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
142 return rcFCB;
143 }
144
145 //FIXME: need to compare against short name in FCB here
146
147 current_entry = current_entry->Flink;
148 }
149 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
150
151 return NULL;
152 }
153
154 NTSTATUS
155 vfatFCBInitializeCache (PVCB vcb, PVFATFCB fcb)
156 {
157 NTSTATUS status;
158 PFILE_OBJECT fileObject;
159 ULONG fileCacheQuantum;
160 PVFATCCB newCCB;
161
162 fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
163
164 newCCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATCCB), TAG_CCB);
165 if (newCCB == NULL)
166 {
167 return STATUS_INSUFFICIENT_RESOURCES;
168 }
169 memset (newCCB, 0, sizeof (VFATCCB));
170
171 fileObject->Flags = fileObject->Flags | FO_FCB_IS_VALID |
172 FO_DIRECT_CACHE_PAGING_READ;
173 fileObject->SectionObjectPointers = &fcb->SectionObjectPointers;
174 fileObject->FsContext = (PVOID) &fcb->RFCB;
175 fileObject->FsContext2 = newCCB;
176 newCCB->pFcb = fcb;
177 newCCB->PtrFileObject = fileObject;
178 fcb->FileObject = fileObject;
179 fcb->pDevExt = vcb;
180
181
182 fileCacheQuantum = (vcb->FatInfo.BytesPerCluster >= PAGESIZE) ?
183 vcb->FatInfo.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->FatInfo.rootDirectorySectors * BLOCKSIZE;
210 FCB->entry.Attrib = FILE_ATTRIBUTE_DIRECTORY;
211 if (pVCB->FatInfo.FatType == FAT32)
212 {
213 CurrentCluster = FirstCluster = pVCB->FatInfo.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->FatInfo.BytesPerCluster;
221 Status = NextCluster (pVCB, NULL, FirstCluster, &CurrentCluster, FALSE);
222 }
223 }
224 else
225 {
226 FCB->entry.FirstCluster = 1;
227 Size = pVCB->FatInfo.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->FatInfo.rootDirectorySectors * BLOCKSIZE;
300 }
301 else
302 {
303 CurrentCluster = FirstCluster;
304 while (CurrentCluster != 0xffffffff)
305 {
306 Size += vcb->FatInfo.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->FatInfo.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 fileCacheQuantum;
355
356 fileCacheQuantum = (vcb->FatInfo.BytesPerCluster >= PAGESIZE) ?
357 vcb->FatInfo.BytesPerCluster : PAGESIZE;
358 status = CcRosInitializeFileCache (fileObject,
359 &fcb->RFCB.Bcb,
360 fileCacheQuantum);
361 if (!NT_SUCCESS (status))
362 {
363 DbgPrint ("CcRosInitializeFileCache failed\n");
364 KeBugCheck (0);
365 }
366 fcb->Flags |= FCB_CACHE_INITIALIZED;
367 }
368
369 DPRINT ("file open: fcb:%x file size: %d\n", fcb, fcb->entry.FileSize);
370
371 return STATUS_SUCCESS;
372 }
373
374 NTSTATUS
375 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt,
376 PVFATFCB pDirectoryFCB,
377 PWSTR pFileToFind,
378 PVFATFCB * pFoundFCB)
379 {
380 BOOL finishedScanningDirectory;
381 ULONG directoryIndex;
382 NTSTATUS status;
383 WCHAR defaultFileName [2];
384 WCHAR currentLongName [256];
385 FAT_DIR_ENTRY currentDirEntry;
386 WCHAR currentEntryName [256];
387
388 assert (pDeviceExt);
389 assert (pDirectoryFCB);
390 assert (pFileToFind);
391
392 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
393 pDeviceExt,
394 pDirectoryFCB,
395 pFileToFind);
396 DPRINT ("Dir Path:%S\n", pDirectoryFCB->PathName);
397
398 // default to '.' if no filename specified
399 if (wcslen (pFileToFind) == 0)
400 {
401 defaultFileName [0] = L'.';
402 defaultFileName [1] = 0;
403 pFileToFind = defaultFileName;
404 }
405
406 directoryIndex = 0;
407 finishedScanningDirectory = FALSE;
408 while (!finishedScanningDirectory)
409 {
410 status = vfatGetNextDirEntry (pDeviceExt,
411 pDirectoryFCB,
412 &directoryIndex,
413 currentLongName,
414 &currentDirEntry);
415 if (status == STATUS_NO_MORE_ENTRIES)
416 {
417 finishedScanningDirectory = TRUE;
418 continue;
419 }
420 else if (!NT_SUCCESS(status))
421 {
422 return status;
423 }
424
425 DPRINT (" Index:%d longName:%S\n",
426 directoryIndex,
427 currentLongName);
428
429 if (!vfatIsDirEntryDeleted (&currentDirEntry)
430 && !vfatIsDirEntryVolume(&currentDirEntry))
431 {
432 if (currentLongName [0] != L'\0' && wstrcmpjoki (currentLongName, pFileToFind))
433 {
434 DPRINT ("Match found, %S\n", currentLongName);
435 status = vfatMakeFCBFromDirEntry (pDeviceExt,
436 pDirectoryFCB,
437 currentLongName,
438 &currentDirEntry,
439 directoryIndex - 1,
440 pFoundFCB);
441 return status;
442 }
443 else
444 {
445 vfatGetDirEntryName (&currentDirEntry, currentEntryName);
446 DPRINT (" entryName:%S\n", currentEntryName);
447
448 if (wstrcmpjoki (currentEntryName, pFileToFind))
449 {
450 DPRINT ("Match found, %S\n", currentEntryName);
451 status = vfatMakeFCBFromDirEntry (pDeviceExt,
452 pDirectoryFCB,
453 currentLongName,
454 &currentDirEntry,
455 directoryIndex - 1,
456 pFoundFCB);
457 return status;
458 }
459 }
460 }
461 }
462
463 return STATUS_OBJECT_NAME_NOT_FOUND;
464 }
465
466 NTSTATUS
467 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB,
468 PVFATFCB *pParentFCB,
469 PVFATFCB *pFCB,
470 const PWSTR pFileName)
471 {
472 NTSTATUS status;
473 WCHAR pathName [MAX_PATH];
474 WCHAR elementName [MAX_PATH];
475 PWCHAR currentElement;
476 PVFATFCB FCB;
477 PVFATFCB parentFCB;
478
479 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
480 pVCB,
481 pParentFCB,
482 pFCB,
483 pFileName);
484
485 // Trivial case, open of the root directory on volume
486 if (pFileName [0] == L'\0' || wcscmp (pFileName, L"\\") == 0)
487 {
488 DPRINT ("returning root FCB\n");
489
490 FCB = vfatOpenRootFCB (pVCB);
491 *pFCB = FCB;
492 *pParentFCB = NULL;
493
494 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
495 }
496 else
497 {
498 currentElement = pFileName + 1;
499 wcscpy (pathName, L"\\");
500 FCB = vfatOpenRootFCB (pVCB);
501 }
502 parentFCB = NULL;
503
504 // Parse filename and check each path element for existance and access
505 while (vfatGetNextPathElement (currentElement) != 0)
506 {
507 // Skip blank directory levels
508 if ((vfatGetNextPathElement (currentElement) - currentElement) == 0)
509 {
510 currentElement++;
511 continue;
512 }
513
514 DPRINT ("Parsing, currentElement:%S\n", currentElement);
515 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB, FCB);
516
517 // descend to next directory level
518 if (parentFCB)
519 {
520 vfatReleaseFCB (pVCB, parentFCB);
521 parentFCB = 0;
522 }
523 // fail if element in FCB is not a directory
524 if (!vfatFCBIsDirectory (pVCB, FCB))
525 {
526 DPRINT ("Element in requested path is not a directory\n");
527
528 vfatReleaseFCB (pVCB, FCB);
529 FCB = 0;
530 *pParentFCB = NULL;
531 *pFCB = NULL;
532
533 return STATUS_OBJECT_PATH_NOT_FOUND;
534 }
535 parentFCB = FCB;
536
537 // Extract next directory level into dirName
538 vfatWSubString (pathName,
539 pFileName,
540 vfatGetNextPathElement (currentElement) - pFileName);
541 DPRINT (" pathName:%S\n", pathName);
542
543 FCB = vfatGrabFCBFromTable (pVCB, pathName);
544 if (FCB == NULL)
545 {
546 vfatWSubString (elementName,
547 currentElement,
548 vfatGetNextPathElement (currentElement) - currentElement);
549 DPRINT (" elementName:%S\n", elementName);
550
551 status = vfatDirFindFile (pVCB, parentFCB, elementName, &FCB);
552 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
553 {
554 *pParentFCB = parentFCB;
555 *pFCB = NULL;
556 currentElement = vfatGetNextPathElement(currentElement);
557 if (*currentElement == L'\0' || vfatGetNextPathElement(currentElement + 1) == 0)
558 {
559 return STATUS_OBJECT_NAME_NOT_FOUND;
560 }
561 else
562 {
563 return STATUS_OBJECT_PATH_NOT_FOUND;
564 }
565 }
566 else if (!NT_SUCCESS (status))
567 {
568 vfatReleaseFCB (pVCB, parentFCB);
569 *pParentFCB = NULL;
570 *pFCB = NULL;
571
572 return status;
573 }
574 }
575 currentElement = vfatGetNextPathElement (currentElement);
576 }
577
578 *pParentFCB = parentFCB;
579 *pFCB = FCB;
580
581 return STATUS_SUCCESS;
582 }
583
584
585
586