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