[NTFS]
[reactos.git] / reactos / drivers / filesystems / ntfs / fcb.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/ntfs/fcb.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMER: Eric Kohl
24 */
25
26 /* INCLUDES *****************************************************************/
27
28 #include "ntfs.h"
29
30 #define NDEBUG
31 #include <debug.h>
32
33 /* MACROS *******************************************************************/
34
35 #define TAG_FCB 'BCFI'
36
37 /* FUNCTIONS ****************************************************************/
38
39 static
40 PWCHAR
41 NtfsGetNextPathElement(PWCHAR FileName)
42 {
43 if (*FileName == L'\0')
44 {
45 return NULL;
46 }
47
48 while (*FileName != L'\0' && *FileName != L'\\')
49 {
50 FileName++;
51 }
52
53 return FileName;
54 }
55
56
57 static
58 VOID
59 NtfsWSubString(PWCHAR pTarget,
60 const PWCHAR pSource,
61 size_t pLength)
62 {
63 wcsncpy(pTarget, pSource, pLength);
64 pTarget[pLength] = L'\0';
65 }
66
67
68 PNTFS_FCB
69 NtfsCreateFCB(PCWSTR FileName,
70 PNTFS_VCB Vcb)
71 {
72 PNTFS_FCB Fcb;
73
74 ASSERT(Vcb);
75 ASSERT(Vcb->Identifier.Type == NTFS_TYPE_VCB);
76
77 Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_FCB), TAG_FCB);
78 RtlZeroMemory(Fcb, sizeof(NTFS_FCB));
79
80 Fcb->Identifier.Type = NTFS_TYPE_FCB;
81 Fcb->Identifier.Size = sizeof(NTFS_TYPE_FCB);
82
83 Fcb->Vcb = Vcb;
84
85 if (FileName)
86 {
87 wcscpy(Fcb->PathName, FileName);
88 if (wcsrchr(Fcb->PathName, '\\') != 0)
89 {
90 Fcb->ObjectName = wcsrchr(Fcb->PathName, '\\');
91 }
92 else
93 {
94 Fcb->ObjectName = Fcb->PathName;
95 }
96 }
97
98 ExInitializeResourceLite(&Fcb->MainResource);
99
100 Fcb->RFCB.Resource = &(Fcb->MainResource);
101
102 return Fcb;
103 }
104
105
106 VOID
107 NtfsDestroyFCB(PNTFS_FCB Fcb)
108 {
109 ASSERT(Fcb);
110 ASSERT(Fcb->Identifier.Type == NTFS_TYPE_FCB);
111
112 ExDeleteResourceLite(&Fcb->MainResource);
113
114 ExFreePool(Fcb);
115 }
116
117
118 BOOLEAN
119 NtfsFCBIsDirectory(PNTFS_FCB Fcb)
120 {
121 return ((Fcb->Entry.FileAttributes & NTFS_FILE_TYPE_DIRECTORY) == NTFS_FILE_TYPE_DIRECTORY);
122 }
123
124
125 BOOLEAN
126 NtfsFCBIsRoot(PNTFS_FCB Fcb)
127 {
128 return (wcscmp(Fcb->PathName, L"\\") == 0);
129 }
130
131
132 VOID
133 NtfsGrabFCB(PNTFS_VCB Vcb,
134 PNTFS_FCB Fcb)
135 {
136 KIRQL oldIrql;
137
138 DPRINT("grabbing FCB at %p: %S, refCount:%d\n",
139 Fcb,
140 Fcb->PathName,
141 Fcb->RefCount);
142
143 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
144 Fcb->RefCount++;
145 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
146 }
147
148
149 VOID
150 NtfsReleaseFCB(PNTFS_VCB Vcb,
151 PNTFS_FCB Fcb)
152 {
153 KIRQL oldIrql;
154
155 DPRINT("releasing FCB at %p: %S, refCount:%d\n",
156 Fcb,
157 Fcb->PathName,
158 Fcb->RefCount);
159
160 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
161 Fcb->RefCount--;
162 if (Fcb->RefCount <= 0 && !NtfsFCBIsDirectory(Fcb))
163 {
164 RemoveEntryList(&Fcb->FcbListEntry);
165 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
166 CcUninitializeCacheMap(Fcb->FileObject, NULL, NULL);
167 NtfsDestroyFCB(Fcb);
168 }
169 else
170 {
171 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
172 }
173 }
174
175
176 VOID
177 NtfsAddFCBToTable(PNTFS_VCB Vcb,
178 PNTFS_FCB Fcb)
179 {
180 KIRQL oldIrql;
181
182 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
183 Fcb->Vcb = Vcb;
184 InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry);
185 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
186 }
187
188
189 PNTFS_FCB
190 NtfsGrabFCBFromTable(PNTFS_VCB Vcb,
191 PCWSTR FileName)
192 {
193 KIRQL oldIrql;
194 PNTFS_FCB Fcb;
195 PLIST_ENTRY current_entry;
196
197 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
198
199 if (FileName == NULL || *FileName == 0)
200 {
201 DPRINT("Return FCB for stream file object\n");
202 Fcb = Vcb->StreamFileObject->FsContext;
203 Fcb->RefCount++;
204 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
205 return Fcb;
206 }
207
208 current_entry = Vcb->FcbListHead.Flink;
209 while (current_entry != &Vcb->FcbListHead)
210 {
211 Fcb = CONTAINING_RECORD(current_entry, NTFS_FCB, FcbListEntry);
212
213 DPRINT("Comparing '%S' and '%S'\n", FileName, Fcb->PathName);
214 if (_wcsicmp(FileName, Fcb->PathName) == 0)
215 {
216 Fcb->RefCount++;
217 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
218 return Fcb;
219 }
220
221 //FIXME: need to compare against short name in FCB here
222
223 current_entry = current_entry->Flink;
224 }
225
226 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
227
228 return NULL;
229 }
230
231
232 NTSTATUS
233 NtfsFCBInitializeCache(PNTFS_VCB Vcb,
234 PNTFS_FCB Fcb)
235 {
236 PFILE_OBJECT FileObject;
237 NTSTATUS Status;
238 PNTFS_CCB newCCB;
239
240 FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice);
241
242 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_CCB), TAG_CCB);
243 if (newCCB == NULL)
244 {
245 return STATUS_INSUFFICIENT_RESOURCES;
246 }
247
248 RtlZeroMemory(newCCB, sizeof(NTFS_CCB));
249
250 newCCB->Identifier.Type = NTFS_TYPE_CCB;
251 newCCB->Identifier.Size = sizeof(NTFS_TYPE_CCB);
252
253 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
254 FileObject->FsContext = Fcb;
255 FileObject->FsContext2 = newCCB;
256 newCCB->PtrFileObject = FileObject;
257 Fcb->FileObject = FileObject;
258 Fcb->Vcb = Vcb;
259
260 Status = STATUS_SUCCESS;
261 CcInitializeCacheMap(FileObject,
262 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
263 FALSE,
264 &(NtfsGlobalData->CacheMgrCallbacks),
265 Fcb);
266
267 ObDereferenceObject(FileObject);
268 Fcb->Flags |= FCB_CACHE_INITIALIZED;
269
270 return Status;
271 }
272
273
274 PNTFS_FCB
275 NtfsMakeRootFCB(PNTFS_VCB Vcb)
276 {
277 PNTFS_FCB Fcb;
278 PFILE_RECORD_HEADER MftRecord;
279 PFILENAME_ATTRIBUTE FileName;
280
281 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
282 Vcb->NtfsInfo.BytesPerFileRecord,
283 TAG_NTFS);
284 if (MftRecord == NULL)
285 {
286 return NULL;
287 }
288
289 if (!NT_SUCCESS(ReadFileRecord(Vcb, NTFS_FILE_ROOT, MftRecord)))
290 {
291 ExFreePoolWithTag(MftRecord, TAG_NTFS);
292 return NULL;
293 }
294
295 FileName = GetFileNameFromRecord(MftRecord, NTFS_FILE_NAME_WIN32);
296 if (!FileName)
297 {
298 ExFreePoolWithTag(MftRecord, TAG_NTFS);
299 return NULL;
300 }
301
302 Fcb = NtfsCreateFCB(L"\\", Vcb);
303 if (!Fcb)
304 {
305 ExFreePoolWithTag(MftRecord, TAG_NTFS);
306 return NULL;
307 }
308
309 memcpy(&Fcb->Entry, FileName, FIELD_OFFSET(FILENAME_ATTRIBUTE, NameLength));
310 Fcb->Entry.NameType = FileName->NameType;
311 Fcb->Entry.NameLength = 0;
312 Fcb->Entry.Name[0] = UNICODE_NULL;
313 Fcb->RefCount = 1;
314 Fcb->DirIndex = 0;
315 Fcb->RFCB.FileSize.QuadPart = FileName->DataSize;
316 Fcb->RFCB.ValidDataLength.QuadPart = FileName->DataSize;
317 Fcb->RFCB.AllocationSize.QuadPart = FileName->AllocatedSize;
318 Fcb->MFTIndex = NTFS_FILE_ROOT;
319
320 NtfsFCBInitializeCache(Vcb, Fcb);
321 NtfsAddFCBToTable(Vcb, Fcb);
322 NtfsGrabFCB(Vcb, Fcb);
323
324 ExFreePoolWithTag(MftRecord, TAG_NTFS);
325
326 return Fcb;
327 }
328
329
330 PNTFS_FCB
331 NtfsOpenRootFCB(PNTFS_VCB Vcb)
332 {
333 PNTFS_FCB Fcb;
334
335 Fcb = NtfsGrabFCBFromTable(Vcb, L"\\");
336 if (Fcb == NULL)
337 {
338 Fcb = NtfsMakeRootFCB(Vcb);
339 }
340
341 return Fcb;
342 }
343
344
345 #if 0
346 static VOID
347 NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt,
348 PDIR_RECORD Record,
349 PWSTR Name)
350 /*
351 * FUNCTION: Retrieves the file name, be it in short or long file name format
352 */
353 {
354 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
355 {
356 wcscpy(Name, L".");
357 }
358 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
359 {
360 wcscpy(Name, L"..");
361 }
362 else
363 {
364 if (DeviceExt->CdInfo.JolietLevel == 0)
365 {
366 ULONG i;
367
368 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
369 Name[i] = (WCHAR)Record->FileId[i];
370 Name[i] = 0;
371 }
372 else
373 {
374 NtfsSwapString(Name, Record->FileId, Record->FileIdLength);
375 }
376 }
377
378 DPRINT("Name '%S'\n", Name);
379 }
380 #endif
381
382
383 NTSTATUS
384 NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb,
385 PNTFS_FCB DirectoryFCB,
386 PUNICODE_STRING Name,
387 PFILE_RECORD_HEADER Record,
388 ULONGLONG MFTIndex,
389 PNTFS_FCB * fileFCB)
390 {
391 WCHAR pathName[MAX_PATH];
392 PFILENAME_ATTRIBUTE FileName;
393 PNTFS_FCB rcFCB;
394
395 DPRINT1("NtfsMakeFCBFromDirEntry(%p, %p, %wZ, %p, %p)\n", Vcb, DirectoryFCB, Name, Record, fileFCB);
396
397 FileName = GetFileNameFromRecord(Record, NTFS_FILE_NAME_WIN32);
398 if (!FileName)
399 {
400 return STATUS_OBJECT_NAME_NOT_FOUND; // Not sure that's the best here
401 }
402
403 if (Name->Buffer[0] != 0 && wcslen(DirectoryFCB->PathName) +
404 sizeof(WCHAR) + Name->Length / sizeof(WCHAR) > MAX_PATH)
405 {
406 return STATUS_OBJECT_NAME_INVALID;
407 }
408
409 wcscpy(pathName, DirectoryFCB->PathName);
410 if (!NtfsFCBIsRoot(DirectoryFCB))
411 {
412 wcscat(pathName, L"\\");
413 }
414 wcscat(pathName, Name->Buffer);
415
416 rcFCB = NtfsCreateFCB(pathName, Vcb);
417 if (!rcFCB)
418 {
419 return STATUS_INSUFFICIENT_RESOURCES;
420 }
421
422 memcpy(&rcFCB->Entry, FileName, FIELD_OFFSET(FILENAME_ATTRIBUTE, NameLength));
423 rcFCB->Entry.NameType = FileName->NameType;
424 rcFCB->RFCB.FileSize.QuadPart = FileName->DataSize;
425 rcFCB->RFCB.ValidDataLength.QuadPart = FileName->DataSize;
426 rcFCB->RFCB.AllocationSize.QuadPart = FileName->AllocatedSize;
427
428 NtfsFCBInitializeCache(Vcb, rcFCB);
429 rcFCB->RefCount = 1;
430 rcFCB->MFTIndex = MFTIndex;
431 NtfsAddFCBToTable(Vcb, rcFCB);
432 *fileFCB = rcFCB;
433
434 return STATUS_SUCCESS;
435 }
436
437
438 NTSTATUS
439 NtfsAttachFCBToFileObject(PNTFS_VCB Vcb,
440 PNTFS_FCB Fcb,
441 PFILE_OBJECT FileObject)
442 {
443 PNTFS_CCB newCCB;
444
445 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_CCB), TAG_CCB);
446 if (newCCB == NULL)
447 {
448 return STATUS_INSUFFICIENT_RESOURCES;
449 }
450
451 RtlZeroMemory(newCCB, sizeof(NTFS_CCB));
452
453 newCCB->Identifier.Type = NTFS_TYPE_CCB;
454 newCCB->Identifier.Size = sizeof(NTFS_TYPE_CCB);
455
456 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
457 FileObject->FsContext = Fcb;
458 FileObject->FsContext2 = newCCB;
459 newCCB->PtrFileObject = FileObject;
460 Fcb->Vcb = Vcb;
461
462 if (!(Fcb->Flags & FCB_CACHE_INITIALIZED))
463 {
464 CcInitializeCacheMap(FileObject,
465 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
466 FALSE,
467 NULL,
468 NULL);
469
470 Fcb->Flags |= FCB_CACHE_INITIALIZED;
471 }
472
473 //DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
474
475 return STATUS_SUCCESS;
476 }
477
478
479 static NTSTATUS
480 NtfsDirFindFile(PNTFS_VCB Vcb,
481 PNTFS_FCB DirectoryFcb,
482 PWSTR FileToFind,
483 PNTFS_FCB *FoundFCB)
484 {
485 NTSTATUS Status;
486 ULONGLONG CurrentDir;
487 UNICODE_STRING File;
488 PFILE_RECORD_HEADER FileRecord;
489 PNTFS_ATTR_CONTEXT DataContext;
490 ULONGLONG MFTIndex;
491
492 DPRINT1("NtfsDirFindFile(%p, %p, %S, %p)\n", Vcb, DirectoryFcb, FileToFind, FoundFCB);
493
494 *FoundFCB = NULL;
495 RtlInitUnicodeString(&File, FileToFind);
496 CurrentDir = DirectoryFcb->MFTIndex;
497
498 Status = NtfsLookupFileAt(Vcb, &File, &FileRecord, &DataContext, &MFTIndex, CurrentDir);
499 if (!NT_SUCCESS(Status))
500 {
501 return Status;
502 }
503
504 Status = NtfsMakeFCBFromDirEntry(Vcb, DirectoryFcb, &File, FileRecord, MFTIndex, FoundFCB);
505 ExFreePoolWithTag(FileRecord, TAG_NTFS);
506
507 return Status;
508 }
509
510
511 NTSTATUS
512 NtfsGetFCBForFile(PNTFS_VCB Vcb,
513 PNTFS_FCB *pParentFCB,
514 PNTFS_FCB *pFCB,
515 const PWSTR pFileName)
516 {
517 NTSTATUS Status;
518 WCHAR pathName [MAX_PATH];
519 WCHAR elementName [MAX_PATH];
520 PWCHAR currentElement;
521 PNTFS_FCB FCB;
522 PNTFS_FCB parentFCB;
523
524 DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S')\n",
525 Vcb,
526 pParentFCB,
527 pFCB,
528 pFileName);
529
530 /* Dummy code */
531 // FCB = NtfsOpenRootFCB(Vcb);
532 // *pFCB = FCB;
533 // *pParentFCB = NULL;
534
535 #if 1
536 /* Trivial case, open of the root directory on volume */
537 if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
538 {
539 DPRINT("returning root FCB\n");
540
541 FCB = NtfsOpenRootFCB(Vcb);
542 *pFCB = FCB;
543 *pParentFCB = NULL;
544
545 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
546 }
547 else
548 {
549 currentElement = pFileName + 1;
550 wcscpy (pathName, L"\\");
551 FCB = NtfsOpenRootFCB (Vcb);
552 }
553
554 parentFCB = NULL;
555
556 /* Parse filename and check each path element for existance and access */
557 while (NtfsGetNextPathElement(currentElement) != 0)
558 {
559 /* Skip blank directory levels */
560 if ((NtfsGetNextPathElement(currentElement) - currentElement) == 0)
561 {
562 currentElement++;
563 continue;
564 }
565
566 DPRINT("Parsing, currentElement:%S\n", currentElement);
567 DPRINT(" parentFCB:%p FCB:%p\n", parentFCB, FCB);
568
569 /* Descend to next directory level */
570 if (parentFCB)
571 {
572 NtfsReleaseFCB(Vcb, parentFCB);
573 parentFCB = NULL;
574 }
575
576 /* fail if element in FCB is not a directory */
577 if (!NtfsFCBIsDirectory(FCB))
578 {
579 DPRINT("Element in requested path is not a directory\n");
580
581 NtfsReleaseFCB(Vcb, FCB);
582 FCB = 0;
583 *pParentFCB = NULL;
584 *pFCB = NULL;
585
586 return STATUS_OBJECT_PATH_NOT_FOUND;
587 }
588
589 parentFCB = FCB;
590
591 /* Extract next directory level into dirName */
592 NtfsWSubString(pathName,
593 pFileName,
594 NtfsGetNextPathElement(currentElement) - pFileName);
595 DPRINT(" pathName:%S\n", pathName);
596
597 FCB = NtfsGrabFCBFromTable(Vcb, pathName);
598 if (FCB == NULL)
599 {
600 NtfsWSubString(elementName,
601 currentElement,
602 NtfsGetNextPathElement(currentElement) - currentElement);
603 DPRINT(" elementName:%S\n", elementName);
604
605 Status = NtfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
606 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
607 {
608 *pParentFCB = parentFCB;
609 *pFCB = NULL;
610 currentElement = NtfsGetNextPathElement(currentElement);
611 if (*currentElement == L'\0' || NtfsGetNextPathElement(currentElement + 1) == 0)
612 {
613 return STATUS_OBJECT_NAME_NOT_FOUND;
614 }
615 else
616 {
617 return STATUS_OBJECT_PATH_NOT_FOUND;
618 }
619 }
620 else if (!NT_SUCCESS(Status))
621 {
622 NtfsReleaseFCB(Vcb, parentFCB);
623 *pParentFCB = NULL;
624 *pFCB = NULL;
625
626 return Status;
627 }
628 }
629
630 currentElement = NtfsGetNextPathElement(currentElement);
631 }
632
633 *pParentFCB = parentFCB;
634 *pFCB = FCB;
635 #endif
636
637 return STATUS_SUCCESS;
638 }
639
640 /* EOF */