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