* Sync up to trunk head (r64939).
[reactos.git] / 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 CcUninitializeCacheMap(Fcb->FileObject, NULL, NULL);
166 NtfsDestroyFCB(Fcb);
167 }
168
169 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
170 }
171
172
173 VOID
174 NtfsAddFCBToTable(PNTFS_VCB Vcb,
175 PNTFS_FCB Fcb)
176 {
177 KIRQL oldIrql;
178
179 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
180 Fcb->Vcb = Vcb;
181 InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry);
182 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
183 }
184
185
186 PNTFS_FCB
187 NtfsGrabFCBFromTable(PNTFS_VCB Vcb,
188 PCWSTR FileName)
189 {
190 KIRQL oldIrql;
191 PNTFS_FCB Fcb;
192 PLIST_ENTRY current_entry;
193
194 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
195
196 if (FileName == NULL || *FileName == 0)
197 {
198 DPRINT("Return FCB for stream file object\n");
199 Fcb = Vcb->StreamFileObject->FsContext;
200 Fcb->RefCount++;
201 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
202 return Fcb;
203 }
204
205 current_entry = Vcb->FcbListHead.Flink;
206 while (current_entry != &Vcb->FcbListHead)
207 {
208 Fcb = CONTAINING_RECORD(current_entry, NTFS_FCB, FcbListEntry);
209
210 DPRINT("Comparing '%S' and '%S'\n", FileName, Fcb->PathName);
211 if (_wcsicmp(FileName, Fcb->PathName) == 0)
212 {
213 Fcb->RefCount++;
214 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
215 return Fcb;
216 }
217
218 //FIXME: need to compare against short name in FCB here
219
220 current_entry = current_entry->Flink;
221 }
222
223 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
224
225 return NULL;
226 }
227
228
229 NTSTATUS
230 NtfsFCBInitializeCache(PNTFS_VCB Vcb,
231 PNTFS_FCB Fcb)
232 {
233 PFILE_OBJECT FileObject;
234 NTSTATUS Status;
235 PNTFS_CCB newCCB;
236
237 FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice);
238
239 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_CCB), TAG_CCB);
240 if (newCCB == NULL)
241 {
242 return STATUS_INSUFFICIENT_RESOURCES;
243 }
244
245 RtlZeroMemory(newCCB, sizeof(NTFS_CCB));
246
247 newCCB->Identifier.Type = NTFS_TYPE_CCB;
248 newCCB->Identifier.Size = sizeof(NTFS_TYPE_CCB);
249
250 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
251 FileObject->FsContext = Fcb;
252 FileObject->FsContext2 = newCCB;
253 newCCB->PtrFileObject = FileObject;
254 Fcb->FileObject = FileObject;
255 Fcb->Vcb = Vcb;
256
257 Status = STATUS_SUCCESS;
258 CcInitializeCacheMap(FileObject,
259 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
260 FALSE,
261 &(NtfsGlobalData->CacheMgrCallbacks),
262 Fcb);
263
264 ObDereferenceObject(FileObject);
265 Fcb->Flags |= FCB_CACHE_INITIALIZED;
266
267 return Status;
268 }
269
270
271 PNTFS_FCB
272 NtfsMakeRootFCB(PNTFS_VCB Vcb)
273 {
274 PNTFS_FCB Fcb;
275 PFILE_RECORD_HEADER MftRecord;
276 PFILENAME_ATTRIBUTE FileName;
277
278 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
279 Vcb->NtfsInfo.BytesPerFileRecord,
280 TAG_NTFS);
281 if (MftRecord == NULL)
282 {
283 return NULL;
284 }
285
286 if (!NT_SUCCESS(ReadFileRecord(Vcb, NTFS_FILE_ROOT, MftRecord)))
287 {
288 ExFreePoolWithTag(MftRecord, TAG_NTFS);
289 return NULL;
290 }
291
292 FileName = GetFileNameFromRecord(MftRecord);
293 if (!FileName)
294 {
295 ExFreePoolWithTag(MftRecord, TAG_NTFS);
296 return NULL;
297 }
298
299 Fcb = NtfsCreateFCB(L"\\", Vcb);
300 if (!Fcb)
301 {
302 ExFreePoolWithTag(MftRecord, TAG_NTFS);
303 return NULL;
304 }
305
306 memcpy(&Fcb->Entry, FileName, FIELD_OFFSET(FILENAME_ATTRIBUTE, NameLength));
307 Fcb->Entry.NameType = FileName->NameType;
308 Fcb->Entry.NameLength = 0;
309 Fcb->Entry.Name[0] = UNICODE_NULL;
310 Fcb->RefCount = 1;
311 Fcb->DirIndex = 0;
312 Fcb->RFCB.FileSize.QuadPart = FileName->DataSize;
313 Fcb->RFCB.ValidDataLength.QuadPart = FileName->DataSize;
314 Fcb->RFCB.AllocationSize.QuadPart = FileName->AllocatedSize;
315 Fcb->MFTIndex = NTFS_FILE_ROOT;
316
317 NtfsFCBInitializeCache(Vcb, Fcb);
318 NtfsAddFCBToTable(Vcb, Fcb);
319 NtfsGrabFCB(Vcb, Fcb);
320
321 ExFreePoolWithTag(MftRecord, TAG_NTFS);
322
323 return Fcb;
324 }
325
326
327 PNTFS_FCB
328 NtfsOpenRootFCB(PNTFS_VCB Vcb)
329 {
330 PNTFS_FCB Fcb;
331
332 Fcb = NtfsGrabFCBFromTable(Vcb, L"\\");
333 if (Fcb == NULL)
334 {
335 Fcb = NtfsMakeRootFCB(Vcb);
336 }
337
338 return Fcb;
339 }
340
341
342 #if 0
343 static VOID
344 NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt,
345 PDIR_RECORD Record,
346 PWSTR Name)
347 /*
348 * FUNCTION: Retrieves the file name, be it in short or long file name format
349 */
350 {
351 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
352 {
353 wcscpy(Name, L".");
354 }
355 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
356 {
357 wcscpy(Name, L"..");
358 }
359 else
360 {
361 if (DeviceExt->CdInfo.JolietLevel == 0)
362 {
363 ULONG i;
364
365 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
366 Name[i] = (WCHAR)Record->FileId[i];
367 Name[i] = 0;
368 }
369 else
370 {
371 NtfsSwapString(Name, Record->FileId, Record->FileIdLength);
372 }
373 }
374
375 DPRINT("Name '%S'\n", Name);
376 }
377 #endif
378
379
380 NTSTATUS
381 NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb,
382 PNTFS_FCB DirectoryFCB,
383 PUNICODE_STRING Name,
384 PFILE_RECORD_HEADER Record,
385 ULONGLONG MFTIndex,
386 PNTFS_FCB * fileFCB)
387 {
388 WCHAR pathName[MAX_PATH];
389 PFILENAME_ATTRIBUTE FileName;
390 PNTFS_FCB rcFCB;
391
392 DPRINT1("NtfsMakeFCBFromDirEntry(%p, %p, %wZ, %p, %p)\n", Vcb, DirectoryFCB, Name, Record, fileFCB);
393
394 FileName = GetFileNameFromRecord(Record);
395 if (!FileName)
396 {
397 return STATUS_OBJECT_NAME_NOT_FOUND; // Not sure that's the best here
398 }
399
400 if (Name->Buffer[0] != 0 && wcslen(DirectoryFCB->PathName) +
401 sizeof(WCHAR) + Name->Length / sizeof(WCHAR) > MAX_PATH)
402 {
403 return STATUS_OBJECT_NAME_INVALID;
404 }
405
406 wcscpy(pathName, DirectoryFCB->PathName);
407 if (!NtfsFCBIsRoot(DirectoryFCB))
408 {
409 wcscat(pathName, L"\\");
410 }
411 wcscat(pathName, Name->Buffer);
412
413 rcFCB = NtfsCreateFCB(pathName, Vcb);
414 if (!rcFCB)
415 {
416 return STATUS_INSUFFICIENT_RESOURCES;
417 }
418
419 memcpy(&rcFCB->Entry, FileName, FIELD_OFFSET(FILENAME_ATTRIBUTE, NameLength));
420 rcFCB->Entry.NameType = FileName->NameType;
421 rcFCB->RFCB.FileSize.QuadPart = FileName->DataSize;
422 rcFCB->RFCB.ValidDataLength.QuadPart = FileName->DataSize;
423 rcFCB->RFCB.AllocationSize.QuadPart = FileName->AllocatedSize;
424
425 NtfsFCBInitializeCache(Vcb, rcFCB);
426 rcFCB->RefCount = 1;
427 rcFCB->MFTIndex = MFTIndex;
428 NtfsAddFCBToTable(Vcb, rcFCB);
429 *fileFCB = rcFCB;
430
431 return STATUS_SUCCESS;
432 }
433
434
435 NTSTATUS
436 NtfsAttachFCBToFileObject(PNTFS_VCB Vcb,
437 PNTFS_FCB Fcb,
438 PFILE_OBJECT FileObject)
439 {
440 PNTFS_CCB newCCB;
441
442 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_CCB), TAG_CCB);
443 if (newCCB == NULL)
444 {
445 return STATUS_INSUFFICIENT_RESOURCES;
446 }
447
448 RtlZeroMemory(newCCB, sizeof(NTFS_CCB));
449
450 newCCB->Identifier.Type = NTFS_TYPE_CCB;
451 newCCB->Identifier.Size = sizeof(NTFS_TYPE_CCB);
452
453 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
454 FileObject->FsContext = Fcb;
455 FileObject->FsContext2 = newCCB;
456 newCCB->PtrFileObject = FileObject;
457 Fcb->Vcb = Vcb;
458
459 if (!(Fcb->Flags & FCB_CACHE_INITIALIZED))
460 {
461 CcInitializeCacheMap(FileObject,
462 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
463 FALSE,
464 NULL,
465 NULL);
466
467 Fcb->Flags |= FCB_CACHE_INITIALIZED;
468 }
469
470 //DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
471
472 return STATUS_SUCCESS;
473 }
474
475
476 static NTSTATUS
477 NtfsDirFindFile(PNTFS_VCB Vcb,
478 PNTFS_FCB DirectoryFcb,
479 PWSTR FileToFind,
480 PNTFS_FCB *FoundFCB)
481 {
482 NTSTATUS Status;
483 ULONGLONG CurrentDir;
484 UNICODE_STRING File;
485 PFILE_RECORD_HEADER FileRecord;
486 PNTFS_ATTR_CONTEXT DataContext;
487 ULONGLONG MFTIndex;
488
489 DPRINT1("NtfsDirFindFile(%p, %p, %S, %p)\n", Vcb, DirectoryFcb, FileToFind, FoundFCB);
490
491 *FoundFCB = NULL;
492 RtlInitUnicodeString(&File, FileToFind);
493 CurrentDir = DirectoryFcb->MFTIndex;
494
495 Status = NtfsLookupFileAt(Vcb, &File, &FileRecord, &DataContext, &MFTIndex, CurrentDir);
496 if (!NT_SUCCESS(Status))
497 {
498 return Status;
499 }
500
501 Status = NtfsMakeFCBFromDirEntry(Vcb, DirectoryFcb, &File, FileRecord, MFTIndex, FoundFCB);
502 ExFreePoolWithTag(FileRecord, TAG_NTFS);
503
504 return Status;
505 }
506
507
508 NTSTATUS
509 NtfsGetFCBForFile(PNTFS_VCB Vcb,
510 PNTFS_FCB *pParentFCB,
511 PNTFS_FCB *pFCB,
512 const PWSTR pFileName)
513 {
514 NTSTATUS Status;
515 WCHAR pathName [MAX_PATH];
516 WCHAR elementName [MAX_PATH];
517 PWCHAR currentElement;
518 PNTFS_FCB FCB;
519 PNTFS_FCB parentFCB;
520
521 DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S')\n",
522 Vcb,
523 pParentFCB,
524 pFCB,
525 pFileName);
526
527 /* Dummy code */
528 // FCB = NtfsOpenRootFCB(Vcb);
529 // *pFCB = FCB;
530 // *pParentFCB = NULL;
531
532 #if 1
533 /* Trivial case, open of the root directory on volume */
534 if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
535 {
536 DPRINT("returning root FCB\n");
537
538 FCB = NtfsOpenRootFCB(Vcb);
539 *pFCB = FCB;
540 *pParentFCB = NULL;
541
542 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
543 }
544 else
545 {
546 currentElement = pFileName + 1;
547 wcscpy (pathName, L"\\");
548 FCB = NtfsOpenRootFCB (Vcb);
549 }
550
551 parentFCB = NULL;
552
553 /* Parse filename and check each path element for existance and access */
554 while (NtfsGetNextPathElement(currentElement) != 0)
555 {
556 /* Skip blank directory levels */
557 if ((NtfsGetNextPathElement(currentElement) - currentElement) == 0)
558 {
559 currentElement++;
560 continue;
561 }
562
563 DPRINT("Parsing, currentElement:%S\n", currentElement);
564 DPRINT(" parentFCB:%p FCB:%p\n", parentFCB, FCB);
565
566 /* Descend to next directory level */
567 if (parentFCB)
568 {
569 NtfsReleaseFCB(Vcb, parentFCB);
570 parentFCB = NULL;
571 }
572
573 /* fail if element in FCB is not a directory */
574 if (!NtfsFCBIsDirectory(FCB))
575 {
576 DPRINT("Element in requested path is not a directory\n");
577
578 NtfsReleaseFCB(Vcb, FCB);
579 FCB = 0;
580 *pParentFCB = NULL;
581 *pFCB = NULL;
582
583 return STATUS_OBJECT_PATH_NOT_FOUND;
584 }
585
586 parentFCB = FCB;
587
588 /* Extract next directory level into dirName */
589 NtfsWSubString(pathName,
590 pFileName,
591 NtfsGetNextPathElement(currentElement) - pFileName);
592 DPRINT(" pathName:%S\n", pathName);
593
594 FCB = NtfsGrabFCBFromTable(Vcb, pathName);
595 if (FCB == NULL)
596 {
597 NtfsWSubString(elementName,
598 currentElement,
599 NtfsGetNextPathElement(currentElement) - currentElement);
600 DPRINT(" elementName:%S\n", elementName);
601
602 Status = NtfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
603 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
604 {
605 *pParentFCB = parentFCB;
606 *pFCB = NULL;
607 currentElement = NtfsGetNextPathElement(currentElement);
608 if (*currentElement == L'\0' || NtfsGetNextPathElement(currentElement + 1) == 0)
609 {
610 return STATUS_OBJECT_NAME_NOT_FOUND;
611 }
612 else
613 {
614 return STATUS_OBJECT_PATH_NOT_FOUND;
615 }
616 }
617 else if (!NT_SUCCESS(Status))
618 {
619 NtfsReleaseFCB(Vcb, parentFCB);
620 *pParentFCB = NULL;
621 *pFCB = NULL;
622
623 return Status;
624 }
625 }
626
627 currentElement = NtfsGetNextPathElement(currentElement);
628 }
629
630 *pParentFCB = parentFCB;
631 *pFCB = FCB;
632 #endif
633
634 return STATUS_SUCCESS;
635 }
636
637 /* EOF */