All:
[reactos.git] / reactos / drivers / fs / vfat / dirwr.c
1 /* $Id: dirwr.c,v 1.24 2002/03/18 22:37:12 hbirr Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: services/fs/vfat/dirwr.c
6 * PURPOSE: VFAT Filesystem : write in directory
7 *
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ddk/ntddk.h>
13 #include <ctype.h>
14 #include <wchar.h>
15 #include <string.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20 #include "vfat.h"
21
22 const char *short_illegals=" ;+=[]',\"*\\<>/?:|";
23
24 static BOOLEAN
25 vfatIsShortIllegal(char c)
26 {
27 int i;
28 for (i = 0; short_illegals[i]; i++)
29 if (c == short_illegals[i])
30 return TRUE;
31 return FALSE;
32 }
33
34 NTSTATUS updEntry (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT pFileObject)
35 /*
36 * update an existing FAT entry
37 */
38 {
39 PVOID Context;
40 PVOID Buffer;
41 NTSTATUS status;
42 PVFATFCB pDirFcb = NULL, pFcb = NULL;
43 LARGE_INTEGER Offset;
44
45 DPRINT ("updEntry PathFileName \'%S\'\n",
46 ((PVFATCCB)(pFileObject->FsContext2))->pFcb->PathName);
47 status = vfatGetFCBForFile(DeviceExt, &pDirFcb, &pFcb,
48 ((PVFATCCB)(pFileObject->FsContext2))->pFcb->PathName);
49 if (pFcb != NULL)
50 {
51 vfatReleaseFCB(DeviceExt, pFcb);
52 }
53 if (!NT_SUCCESS(status))
54 {
55 if (pDirFcb != NULL)
56 {
57 vfatReleaseFCB(DeviceExt, pDirFcb);
58 }
59 return status;
60 }
61
62 Offset.u.HighPart = 0;
63 Offset.u.LowPart = pFcb->dirIndex * sizeof(FATDirEntry);
64 if (CcMapData (pDirFcb->FileObject, &Offset, sizeof(FATDirEntry),
65 TRUE, &Context, (PVOID*)&Buffer))
66 {
67 memcpy(Buffer, &pFcb->entry, sizeof(FATDirEntry));
68 CcSetDirtyPinnedData(Context, NULL);
69 CcUnpinData(Context);
70 }
71 else
72 DPRINT1 ("Failed write to \'%S\'.\n", pDirFcb->PathName);
73 vfatReleaseFCB(DeviceExt, pDirFcb);
74 return STATUS_SUCCESS;
75 }
76
77 BOOLEAN
78 findDirSpace(PDEVICE_EXTENSION DeviceExt,
79 PVFATFCB pDirFcb,
80 ULONG nbSlots,
81 PULONG start)
82 {
83 /*
84 * try to find contiguous entries frees in directory,
85 * extend a directory if is neccesary
86 */
87 LARGE_INTEGER FileOffset;
88 ULONG i, count, size, nbFree = 0;
89 FATDirEntry* pFatEntry;
90 PVOID Context = NULL;
91 NTSTATUS Status;
92 FileOffset.QuadPart = 0;
93 count = pDirFcb->RFCB.FileSize.u.LowPart / sizeof(FATDirEntry);
94 size = DeviceExt->FatInfo.BytesPerCluster / sizeof(FATDirEntry);
95 for (i = 0; i < count; i++, pFatEntry++)
96 {
97 if (Context == NULL || (i % size) == 0)
98 {
99 if (Context)
100 {
101 CcUnpinData(Context);
102 }
103 // FIXME: check return value
104 CcMapData (pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
105 TRUE, &Context, (PVOID*)&pFatEntry);
106 FileOffset.u.LowPart += DeviceExt->FatInfo.BytesPerCluster;
107 }
108 if (vfatIsDirEntryEndMarker(pFatEntry))
109 {
110 break;
111 }
112 if (vfatIsDirEntryDeleted(pFatEntry))
113 {
114 nbFree++;
115 }
116 else
117 {
118 nbFree = 0;
119 }
120 if (nbFree == nbSlots)
121 {
122 break;
123 }
124 }
125 if (Context)
126 {
127 CcUnpinData(Context);
128 }
129 if (nbFree == nbSlots)
130 {
131 // found enough contiguous free slots
132 *start = i - nbSlots + 1;
133 }
134 else
135 {
136 *start = i - nbFree;
137 if (*start + nbSlots > count)
138 {
139 CHECKPOINT;
140 // extend the directory
141 if (vfatFCBIsRoot(pDirFcb) && DeviceExt->FatInfo.FatType != FAT32)
142 {
143 // We can't extend a root directory on a FAT12/FAT16 partition
144 return FALSE;
145 }
146 Status = vfatExtendSpace (DeviceExt, pDirFcb->FileObject,
147 pDirFcb->RFCB.FileSize.u.LowPart + DeviceExt->FatInfo.BytesPerCluster);
148 if (!NT_SUCCESS(Status))
149 {
150 return FALSE;
151 }
152 // clear the new dir cluster
153 FileOffset.u.LowPart = pDirFcb->RFCB.FileSize.QuadPart -
154 DeviceExt->FatInfo.BytesPerCluster;
155 CcMapData (pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
156 TRUE, &Context, (PVOID*)&pFatEntry);
157 RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
158 }
159 else if (*start + nbSlots < count)
160 {
161 // clear the entry after the last new entry
162 FileOffset.u.LowPart = (*start + nbSlots) * sizeof(FATDirEntry);
163 CcMapData (pDirFcb->FileObject, &FileOffset, sizeof(FATDirEntry),
164 TRUE, &Context, (PVOID*)&pFatEntry);
165 RtlZeroMemory(pFatEntry, sizeof(FATDirEntry));
166 }
167 CcSetDirtyPinnedData(Context, NULL);
168 CcUnpinData(Context);
169 }
170 DPRINT ("nbSlots %d nbFree %d, entry number %d\n", nbSlots, nbFree, *start);
171 return TRUE;
172 }
173
174 NTSTATUS
175 addEntry (PDEVICE_EXTENSION DeviceExt,
176 PFILE_OBJECT pFileObject,
177 ULONG RequestedOptions,
178 UCHAR ReqAttr)
179 /*
180 create a new FAT entry
181 */
182 {
183 WCHAR DirName[MAX_PATH], *FileName, *PathFileName;
184 VFATFCB FileFcb;
185 PVOID Context = NULL;
186 FATDirEntry *pFatEntry, *pEntry;
187 slot *pSlots;
188 short nbSlots = 0, nbFree = 0, j, posCar, NameLen;
189 PUCHAR Buffer;
190 BOOLEAN needTilde = FALSE, needLong = FALSE;
191 PVFATFCB newFCB;
192 ULONG CurrentCluster;
193 LARGE_INTEGER SystemTime, LocalTime, FileOffset;
194 NTSTATUS Status = STATUS_SUCCESS;
195 PVFATFCB pDirFcb;
196 ULONG start, size;
197 long i;
198
199 PathFileName = pFileObject->FileName.Buffer;
200 DPRINT ("addEntry: Pathname=%S\n", PathFileName);
201 //find last \ in PathFileName
202 posCar = -1;
203 for (i = 0; PathFileName[i]; i++)
204 {
205 if (PathFileName[i] == L'\\')
206 {
207 posCar = i;
208 }
209 }
210 if (posCar == -1)
211 {
212 return STATUS_UNSUCCESSFUL;
213 }
214 FileName = &PathFileName[posCar + 1];
215 for (NameLen = 0; FileName[NameLen]; NameLen++);
216 // extract directory name from pathname
217 if (posCar == 0)
218 {
219 // root dir
220 DirName[0] = L'\\';
221 DirName[1] = 0;
222 }
223 else
224 {
225 memcpy (DirName, PathFileName, posCar * sizeof (WCHAR));
226 DirName[posCar] = 0;
227 }
228 // open parent directory
229 pDirFcb = vfatGrabFCBFromTable(DeviceExt, DirName);
230 if (pDirFcb == NULL)
231 {
232 return STATUS_UNSUCCESSFUL;
233 }
234 nbSlots = (NameLen + 12) / 13 + 1; //nb of entry needed for long name+normal entry
235 DPRINT ("NameLen= %d, nbSlots =%d\n", NameLen, nbSlots);
236 Buffer = ExAllocatePool (NonPagedPool, nbSlots * sizeof (FATDirEntry));
237 RtlZeroMemory (Buffer, nbSlots * sizeof (FATDirEntry));
238 pEntry = (FATDirEntry *) (Buffer + (nbSlots - 1) * sizeof (FATDirEntry));
239 pSlots = (slot *) Buffer;
240 // create 8.3 name
241 needTilde = FALSE;
242 // find last point in name
243 posCar = j = 0;
244 for (i = 0; FileName[i]; i++)
245 {
246 if (FileName[i] == '.')
247 {
248 posCar = i;
249 if (i == j)
250 {
251 j++;
252 }
253 }
254 }
255 if (!posCar)
256 {
257 posCar = i;
258 }
259 if (posCar < j)
260 {
261 posCar = i;
262 needTilde = TRUE;
263 }
264 if (posCar > 8)
265 {
266 needTilde = TRUE;
267 }
268 //copy 8 characters max
269 memset (pEntry, ' ', 11);
270 for (i = 0, j = 0; j < 8 && i < posCar; i++)
271 {
272 if (vfatIsShortIllegal (FileName[i]))
273 {
274 needTilde = TRUE;
275 pEntry->Filename[j++] = '_';
276 }
277 else
278 {
279 if (FileName[i] == '.')
280 {
281 needTilde = TRUE;
282 }
283 else
284 {
285 pEntry->Filename[j++] = toupper ((char) FileName[i]);
286 }
287 }
288 }
289 //copy extension
290 if (FileName[posCar])
291 {
292 for (j = 0, i = posCar + 1; FileName[i] && j < 3; i++)
293 {
294 if (vfatIsShortIllegal(FileName[i]))
295 {
296 needTilde = TRUE;
297 pEntry->Ext[j++] = '_';
298 }
299 else
300 {
301 if (FileName[i] == '.')
302 {
303 needTilde = TRUE;
304 }
305 else
306 {
307 pEntry->Ext[j++] = toupper ((char) (FileName[i] & 0x7F));
308 }
309 }
310 }
311 }
312 if (FileName[i])
313 {
314 needTilde = TRUE;
315 }
316 //find good value for tilde
317 if (needTilde)
318 {
319 needLong = TRUE;
320 DPRINT ("searching a good value for tilde\n");
321 for (posCar = 0; posCar < 8 && pEntry->Filename[posCar] != ' '; posCar++);
322 if (posCar == 0) // ??????????????????????
323 {
324 pEntry->Filename[posCar++] = '_';
325 }
326 posCar += 2;
327 if (posCar > 8)
328 {
329 posCar = 8;
330 }
331 pEntry->Filename[posCar - 2] = '~';
332 pEntry->Filename[posCar - 1] = '1';
333 vfat8Dot3ToString (pEntry->Filename, pEntry->Ext, DirName);
334 //try first with xxxxxx~y.zzz
335 for (i = 1; i < 10; i++)
336 {
337 DirName[posCar-1] = '0' + i;
338 pEntry->Filename[posCar - 1] = '0' + i;
339 Status = FindFile (DeviceExt, &FileFcb, pDirFcb, DirName, NULL, NULL);
340 if (!NT_SUCCESS(Status))
341 {
342 break;
343 }
344 }
345 if (i == 10)
346 {
347 posCar++;
348 if (posCar > 8)
349 {
350 posCar = 8;
351 }
352 pEntry->Filename[posCar - 3] = '~';
353 pEntry->Filename[posCar - 2] = '1';
354 pEntry->Filename[posCar - 1] = '0';
355 vfat8Dot3ToString (pEntry->Filename, pEntry->Ext, DirName);
356 //try second with xxxxx~yy.zzz
357 for (i = 10; i < 100; i++)
358 {
359 DirName[posCar - 1] = '0' + i % 10;
360 DirName[posCar - 2] = '0' + i / 10;
361 pEntry->Filename[posCar - 1] = '0' + i % 10;
362 pEntry->Filename[posCar - 2] = '0' + i / 10;
363 Status = FindFile (DeviceExt, &FileFcb, pDirFcb, DirName, NULL, NULL);
364 if (!NT_SUCCESS(Status))
365 {
366 break;
367 }
368 }
369 if (i == 100) //FIXME : what to do after 99 tilde ?
370 {
371 vfatReleaseFCB(DeviceExt, pDirFcb);
372 ExFreePool (Buffer);
373 return STATUS_UNSUCCESSFUL;
374 }
375 }
376 }
377 else
378 {
379 DPRINT ("check if long name entry needed, needlong=%d\n", needLong);
380 for (i = 0; i < posCar; i++)
381 {
382 if ((USHORT) pEntry->Filename[i] != FileName[i])
383 {
384 DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
385 needLong = TRUE;
386 }
387 }
388 if (FileName[i])
389 {
390 i++; //jump on point char
391 for (j = 0, i = posCar + 1; FileName[i] && i < posCar + 4; i++)
392 {
393 if ((USHORT) pEntry->Ext[j++] != FileName[i])
394 {
395 DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Filename[i],
396 FileName[i]);
397 needLong = TRUE;
398 }
399 }
400 }
401 }
402 if (needLong == FALSE)
403 {
404 nbSlots = 1;
405 memcpy (Buffer, pEntry, sizeof (FATDirEntry));
406 memset (pEntry, 0, sizeof (FATDirEntry));
407 pEntry = (FATDirEntry *) Buffer;
408 }
409 else
410 {
411 memset (DirName, 0xff, sizeof (DirName));
412 memcpy (DirName, FileName, NameLen * sizeof (WCHAR));
413 DirName[NameLen] = 0;
414 }
415 DPRINT ("dos name=%11.11s\n", pEntry->Filename);
416
417 /* set attributes */
418 pEntry->Attrib = ReqAttr;
419 if (RequestedOptions & FILE_DIRECTORY_FILE)
420 {
421 pEntry->Attrib |= FILE_ATTRIBUTE_DIRECTORY;
422 }
423 /* set dates and times */
424 KeQuerySystemTime (&SystemTime);
425 ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
426 FsdFileTimeToDosDateTime ((TIME *) & LocalTime, &pEntry->CreationDate,
427 &pEntry->CreationTime);
428 pEntry->UpdateDate = pEntry->CreationDate;
429 pEntry->UpdateTime = pEntry->CreationTime;
430 pEntry->AccessDate = pEntry->CreationDate;
431
432 // calculate checksum for 8.3 name
433 for (pSlots[0].alias_checksum = i = 0; i < 11; i++)
434 {
435 pSlots[0].alias_checksum = (((pSlots[0].alias_checksum & 1) << 7
436 | ((pSlots[0].alias_checksum & 0xfe) >> 1))
437 + pEntry->Filename[i]);
438 }
439 //construct slots and entry
440 for (i = nbSlots - 2; i >= 0; i--)
441 {
442 DPRINT ("construct slot %d\n", i);
443 pSlots[i].attr = 0xf;
444 if (i)
445 {
446 pSlots[i].id = nbSlots - i - 1;
447 }
448 else
449 {
450 pSlots[i].id = nbSlots - i - 1 + 0x40;
451 }
452 pSlots[i].alias_checksum = pSlots[0].alias_checksum;
453 //FIXME pSlots[i].start=;
454 memcpy (pSlots[i].name0_4, DirName + (nbSlots - i - 2) * 13, 10);
455 memcpy (pSlots[i].name5_10, DirName + (nbSlots - i - 2) * 13 + 5, 12);
456 memcpy (pSlots[i].name11_12, DirName + (nbSlots - i - 2) * 13 + 11, 4);
457 }
458
459 //try to find nbSlots contiguous entries frees in directory
460 if (!findDirSpace(DeviceExt, pDirFcb, nbSlots, &start))
461 {
462 vfatReleaseFCB(DeviceExt, pDirFcb);
463 ExFreePool (Buffer);
464 return STATUS_DISK_FULL;
465 }
466
467 if (RequestedOptions & FILE_DIRECTORY_FILE)
468 {
469 CurrentCluster = 0xffffffff;
470 Status = NextCluster (DeviceExt, NULL, 0, &CurrentCluster, TRUE);
471 if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
472 {
473 vfatReleaseFCB(DeviceExt, pDirFcb);
474 ExFreePool (Buffer);
475 if (!NT_SUCCESS(Status))
476 {
477 return Status;
478 }
479 return STATUS_DISK_FULL;
480 }
481 if (DeviceExt->FatInfo.FatType == FAT32)
482 {
483 pEntry->FirstClusterHigh = CurrentCluster >> 16;
484 }
485 pEntry->FirstCluster = CurrentCluster;
486 }
487
488 size = DeviceExt->FatInfo.BytesPerCluster / sizeof(FATDirEntry);
489 if (start / size == (start + nbSlots - 1) / size)
490 {
491 // one cluster
492 CHECKPOINT;
493 FileOffset.u.HighPart = 0;
494 FileOffset.u.LowPart = start * sizeof(FATDirEntry);
495 CcMapData (pDirFcb->FileObject, &FileOffset, nbSlots * sizeof(FATDirEntry),
496 TRUE, &Context, (PVOID*)&pFatEntry);
497 memcpy(pFatEntry, Buffer, nbSlots * sizeof(FATDirEntry));
498 }
499 else
500 {
501 // two clusters
502 CHECKPOINT;
503 FileOffset.u.HighPart = 0;
504 FileOffset.u.LowPart = start * sizeof(FATDirEntry);
505 size = DeviceExt->FatInfo.BytesPerCluster -
506 (start * sizeof(FATDirEntry)) % DeviceExt->FatInfo.BytesPerCluster;
507 CcMapData (pDirFcb->FileObject, &FileOffset, size, TRUE,
508 &Context, (PVOID*)&pFatEntry);
509 memcpy(pFatEntry, Buffer, size);
510 CcSetDirtyPinnedData(Context, NULL);
511 CcUnpinData(Context);
512 FileOffset.u.LowPart += size;
513 CcMapData (pDirFcb->FileObject, &FileOffset,
514 nbSlots * sizeof(FATDirEntry) - size,
515 TRUE, &Context, (PVOID*)&pFatEntry);
516 memcpy(pFatEntry, (PVOID)Buffer + size, nbSlots * sizeof(FATDirEntry) - size);
517 }
518 CcSetDirtyPinnedData(Context, NULL);
519 CcUnpinData(Context);
520
521 // FEXME: check status
522 vfatMakeFCBFromDirEntry (DeviceExt, pDirFcb, FileName, pEntry,
523 start + nbSlots - 1, &newFCB);
524 vfatAttachFCBToFileObject (DeviceExt, newFCB, pFileObject);
525
526 DPRINT ("new : entry=%11.11s\n", newFCB->entry.Filename);
527 DPRINT ("new : entry=%11.11s\n", pEntry->Filename);
528
529 if (RequestedOptions & FILE_DIRECTORY_FILE)
530 {
531 FileOffset.QuadPart = 0;
532 CcMapData (pFileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, TRUE,
533 &Context, (PVOID*)&pFatEntry);
534 // clear the new directory cluster
535 RtlZeroMemory (pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
536 // create '.' and '..'
537 memcpy (&pFatEntry[0].Attrib, &pEntry->Attrib, sizeof(FATDirEntry) - 11);
538 memcpy (pFatEntry[0].Filename, ". ", 11);
539 memcpy (&pFatEntry[1].Attrib, &pEntry->Attrib, sizeof(FATDirEntry) - 11);
540 memcpy (pFatEntry[1].Filename, ".. ", 11);
541 pFatEntry[1].FirstCluster = pDirFcb->entry.FirstCluster;
542 pFatEntry[1].FirstClusterHigh = pDirFcb->entry.FirstClusterHigh;
543 if (DeviceExt->FatInfo.FatType == FAT32)
544 {
545 if (pFatEntry[1].FirstCluster == (DeviceExt->FatInfo.RootCluster & 0xffff) &&
546 pFatEntry[1].FirstClusterHigh == (DeviceExt->FatInfo.RootCluster >> 16))
547 {
548 pFatEntry[1].FirstCluster = 0;
549 pFatEntry[1].FirstClusterHigh = 0;
550 }
551 }
552 else
553 {
554 if (pFatEntry[1].FirstCluster == 1)
555 {
556 pFatEntry[1].FirstCluster = 0;
557 }
558 }
559 CcSetDirtyPinnedData(Context, NULL);
560 CcUnpinData(Context);
561 }
562 vfatReleaseFCB (DeviceExt, pDirFcb);
563 ExFreePool (Buffer);
564 DPRINT ("addentry ok\n");
565 return STATUS_SUCCESS;
566 }
567
568 NTSTATUS
569 delEntry (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT pFileObject)
570 /*
571 * deleting an existing FAT entry
572 */
573 {
574 VFATFCB Fcb;
575 PVFATFCB pFcb = NULL, pDirFcb = NULL;
576 NTSTATUS status;
577 PWSTR pName;
578 ULONG Entry = 0, startEntry, Read, CurrentCluster, NextCluster, i;
579 FATDirEntry DirEntry;
580
581 DPRINT ("delEntry PathFileName \'%S\'\n", pFileObject->FileName.Buffer);
582
583 status = vfatGetFCBForFile(DeviceExt, &pDirFcb, &pFcb,
584 pFileObject->FileName.Buffer);
585 if (pFcb != NULL)
586 {
587 vfatReleaseFCB(DeviceExt, pFcb);
588 }
589 if (!NT_SUCCESS(status))
590 {
591 if (pDirFcb != NULL)
592 {
593 vfatReleaseFCB(DeviceExt, pDirFcb);
594 }
595 return status;
596 }
597 pName = ((PVFATCCB)(pFileObject->FsContext2))->pFcb->ObjectName;
598 if (*pName == L'\\')
599 {
600 pName ++;
601 }
602 status = FindFile (DeviceExt, &Fcb, pDirFcb, pName, &Entry, &startEntry);
603
604 if (NT_SUCCESS(status))
605 {
606 PVOID Context = NULL;
607 LARGE_INTEGER Offset;
608 FATDirEntry* pDirEntry;
609 DPRINT ("delete entry: %d to %d\n", startEntry, Entry);
610 Offset.u.HighPart = 0;
611 for (i = startEntry; i <= Entry; i++)
612 {
613 if (Context == NULL || ((i * sizeof(FATDirEntry)) % PAGESIZE) == 0)
614 {
615 if (Context)
616 {
617 CcSetDirtyPinnedData(Context, NULL);
618 CcUnpinData(Context);
619 }
620 Offset.u.LowPart = (i * sizeof(FATDirEntry) / PAGESIZE) * PAGESIZE;
621 CcMapData (pDirFcb->FileObject, &Offset, PAGESIZE, TRUE,
622 &Context, (PVOID*)&pDirEntry);
623 }
624 pDirEntry[i % (PAGESIZE / sizeof(FATDirEntry))].Filename[0] = 0xe5;
625 if (i == Entry)
626 {
627 CurrentCluster =
628 vfatDirEntryGetFirstCluster (DeviceExt,
629 &pDirEntry[i % (PAGESIZE / sizeof(FATDirEntry))]);
630 }
631 }
632 if (Context)
633 {
634 CcSetDirtyPinnedData(Context, NULL);
635 CcUnpinData(Context);
636 }
637
638 while (CurrentCluster && CurrentCluster != 0xffffffff)
639 {
640 GetNextCluster (DeviceExt, CurrentCluster, &NextCluster, FALSE);
641 // FIXME: check status
642 WriteCluster(DeviceExt, CurrentCluster, 0);
643 CurrentCluster = NextCluster;
644 }
645 }
646 vfatReleaseFCB(DeviceExt, pDirFcb);
647 return status;
648 }
649
650 /* EOF */