5d5a183a69d87aa1d41937c9f359ab4efbda6e0b
[reactos.git] / reactos / drivers / fs / vfat / dirwr.c
1 /* $Id: dirwr.c,v 1.23 2002/01/08 00:49:01 dwelch 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 /*
35 * Copies a file name into a directory slot (long file name entry)
36 * and fills trailing slot space with 0xFFFF. This keeps scandisk
37 * from complaining.
38 */
39 static VOID
40 FillSlot (slot * Slot, WCHAR * FileName)
41 {
42 BOOLEAN fill = FALSE;
43 WCHAR *src = FileName;
44 WCHAR *dst;
45 int i;
46
47 i = 5;
48 dst = Slot->name0_4;
49 while (i-- > 0)
50 {
51 if (fill == FALSE)
52 *dst = *src;
53 else
54 *dst = 0xffff;
55
56 if (fill == FALSE && (*src == 0))
57 fill = TRUE;
58 dst++;
59 src++;
60 }
61
62 i = 6;
63 dst = Slot->name5_10;
64 while (i-- > 0)
65 {
66 if (fill == FALSE)
67 *dst = *src;
68 else
69 *dst = 0xffff;
70
71 if (fill == FALSE && (*src == 0))
72 fill = TRUE;
73 dst++;
74 src++;
75 }
76
77 i = 2;
78 dst = Slot->name11_12;
79 while (i-- > 0)
80 {
81 if (fill == FALSE)
82 *dst = *src;
83 else
84 *dst = 0xffff;
85
86 if (fill == FALSE && (*src == 0))
87 fill = TRUE;
88 dst++;
89 src++;
90 }
91 }
92
93 NTSTATUS updEntry (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT pFileObject)
94 /*
95 update an existing FAT entry
96 */
97 {
98 PVOID Context;
99 PVOID Buffer;
100 NTSTATUS status;
101 PVFATFCB pDirFcb = NULL, pFcb = NULL;
102 LARGE_INTEGER Offset;
103
104 DPRINT ("updEntry PathFileName \'%S\'\n",
105 ((PVFATCCB)(pFileObject->FsContext2))->pFcb->PathName);
106 status = vfatGetFCBForFile(DeviceExt, &pDirFcb, &pFcb,
107 ((PVFATCCB)(pFileObject->FsContext2))->pFcb->PathName);
108 if (pFcb != NULL)
109 {
110 vfatReleaseFCB(DeviceExt, pFcb);
111 }
112 if (!NT_SUCCESS(status))
113 {
114 if (pDirFcb != NULL)
115 {
116 vfatReleaseFCB(DeviceExt, pDirFcb);
117 }
118 return status;
119 }
120
121 Offset.QuadPart = pFcb->dirIndex * sizeof(FATDirEntry);
122 if(CcMapData (pDirFcb->FileObject, &Offset, sizeof(FATDirEntry),
123 TRUE, &Context, &Buffer))
124 {
125 memcpy(Buffer, &pFcb->entry, sizeof(FATDirEntry));
126 CcSetDirtyPinnedData(Context, NULL);
127 CcUnpinData(Context);
128 }
129 else
130 DPRINT1 ("Failed write to \'%S\'.\n", pDirFcb->PathName);
131 vfatReleaseFCB(DeviceExt, pDirFcb);
132 return STATUS_SUCCESS;
133 }
134
135 NTSTATUS
136 addEntry (PDEVICE_EXTENSION DeviceExt,
137 PFILE_OBJECT pFileObject, ULONG RequestedOptions, UCHAR ReqAttr)
138 /*
139 create a new FAT entry
140 */
141 {
142 WCHAR DirName[MAX_PATH], *FileName, *PathFileName;
143 VFATFCB FileFcb;
144 FATDirEntry FatEntry;
145 NTSTATUS status;
146 FATDirEntry *pEntry;
147 slot *pSlots;
148 ULONG LengthRead, Offset;
149 short nbSlots = 0, nbFree = 0, i, j, posCar, NameLen;
150 PUCHAR Buffer, Buffer2;
151 BOOLEAN needTilde = FALSE, needLong = FALSE;
152 PVFATFCB newFCB;
153 ULONG CurrentCluster;
154 LARGE_INTEGER SystemTime, LocalTime;
155 NTSTATUS Status = STATUS_SUCCESS;
156 PVFATFCB pDirFcb;
157
158 PathFileName = pFileObject->FileName.Buffer;
159 DPRINT ("addEntry: Pathname=%S\n", PathFileName);
160 //find last \ in PathFileName
161 posCar = -1;
162 for (i = 0; PathFileName[i]; i++)
163 if (PathFileName[i] == '\\')
164 posCar = i;
165 if (posCar == -1)
166 return STATUS_UNSUCCESSFUL;
167 FileName = &PathFileName[posCar + 1];
168 for (NameLen = 0; FileName[NameLen]; NameLen++);
169 // extract directory name from pathname
170 if (posCar == 0)
171 {
172 // root dir
173 DirName[0] = L'\\';
174 DirName[1] = 0;
175 }
176 else
177 {
178 memcpy (DirName, PathFileName, posCar * sizeof (WCHAR));
179 DirName[posCar] = 0;
180 }
181 // open parent directory
182 pDirFcb = vfatGrabFCBFromTable(DeviceExt, DirName);
183 if (pDirFcb == NULL)
184 {
185 return STATUS_UNSUCCESSFUL;
186 }
187 nbSlots = (NameLen + 12) / 13 + 1; //nb of entry needed for long name+normal entry
188 DPRINT ("NameLen= %d, nbSlots =%d\n", NameLen, nbSlots);
189 Buffer =
190 ExAllocatePool (NonPagedPool, (nbSlots + 1) * sizeof (FATDirEntry));
191 memset (Buffer, 0, (nbSlots + 1) * sizeof (FATDirEntry));
192 pEntry = (FATDirEntry *) (Buffer + (nbSlots - 1) * sizeof (FATDirEntry));
193 pSlots = (slot *) Buffer;
194 // create 8.3 name
195 needTilde = FALSE;
196 // find last point in name
197 posCar = j = 0;
198 for (i = 0; FileName[i]; i++)
199 if (FileName[i] == '.')
200 {
201 posCar = i;
202 if (i == j)
203 j++;
204 }
205 if (!posCar)
206 posCar = i;
207 if (posCar < j)
208 {
209 posCar = i;
210 needTilde = TRUE;
211 }
212 if (posCar > 8)
213 needTilde = TRUE;
214 //copy 8 characters max
215 memset (pEntry, ' ', 11);
216 for (i = 0, j = 0; j < 8 && i < posCar; i++)
217 {
218 if (vfatIsShortIllegal (FileName[i]))
219 {
220 needTilde = TRUE;
221 pEntry->Filename[j++] = '_';
222 }
223 else
224 {
225 if (FileName[i] == '.')
226 needTilde = TRUE;
227 else
228 pEntry->Filename[j++] = toupper ((char) FileName[i]);
229 }
230 }
231 //copy extension
232 if (FileName[posCar])
233 for (j = 0, i = posCar + 1; FileName[i] && j < 3; i++)
234 {
235 if (vfatIsShortIllegal(FileName[i]))
236 {
237 needTilde = TRUE;
238 pEntry->Ext[j++] = '_';
239 }
240 else
241 {
242 if (FileName[i] == '.')
243 needTilde = TRUE;
244 else
245 pEntry->Ext[j++] = toupper ((char) (FileName[i] & 0x7F));
246 }
247 }
248 if (FileName[i])
249 needTilde = TRUE;
250 //find good value for tilde
251 if (needTilde)
252 {
253 needLong = TRUE;
254 DPRINT ("searching a good value for tilde\n");
255 for (posCar = 0; posCar < 8 && pEntry->Filename[posCar] != ' '; posCar++);
256 if (posCar == 0) // ??????????????????????
257 pEntry->Filename[posCar++] = '_';
258 posCar += 2;
259 if (posCar > 8)
260 posCar = 8;
261 pEntry->Filename[posCar - 2] = '~';
262 pEntry->Filename[posCar - 1] = '1';
263 vfat8Dot3ToString (pEntry->Filename, pEntry->Ext, DirName);
264 //try first with xxxxxx~y.zzz
265 for (i = 1; i < 10; i++)
266 {
267 DirName[posCar-1] = '0' + i;
268 pEntry->Filename[posCar - 1] = '0' + i;
269 status = FindFile (DeviceExt, &FileFcb, pDirFcb, DirName, NULL, NULL);
270 if (!NT_SUCCESS(status))
271 break;
272 }
273 if (i == 10)
274 {
275 posCar++;
276 if (posCar > 8)
277 posCar = 8;
278 pEntry->Filename[posCar - 3] = '~';
279 pEntry->Filename[posCar - 2] = '1';
280 pEntry->Filename[posCar - 1] = '0';
281 vfat8Dot3ToString (pEntry->Filename, pEntry->Ext, DirName);
282 //try second with xxxxx~yy.zzz
283 for (i = 10; i < 100; i++)
284 {
285 DirName[posCar - 1] = '0' + i % 10;
286 DirName[posCar - 2] = '0' + i / 10;
287 pEntry->Filename[posCar - 1] = '0' + i % 10;
288 pEntry->Filename[posCar - 2] = '0' + i / 10;
289 status = FindFile (DeviceExt, &FileFcb, pDirFcb, DirName, NULL, NULL);
290 if (!NT_SUCCESS(status))
291 break;
292 }
293 if (i == 100) //FIXME : what to do after 99 tilde ?
294 {
295 vfatReleaseFCB(DeviceExt, pDirFcb);
296 ExFreePool (Buffer);
297 return STATUS_UNSUCCESSFUL;
298 }
299 }
300 }
301 else
302 {
303 DPRINT ("check if long name entry needed, needlong=%d\n", needLong);
304 for (i = 0; i < posCar; i++)
305 if ((USHORT) pEntry->Filename[i] != FileName[i])
306 {
307 DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
308 needLong = TRUE;
309 }
310 if (FileName[i])
311 {
312 i++; //jump on point char
313 for (j = 0, i = posCar + 1; FileName[i] && i < posCar + 4; i++)
314 if ((USHORT) pEntry->Ext[j++] != FileName[i])
315 {
316 DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Filename[i],
317 FileName[i]);
318 needLong = TRUE;
319 }
320 }
321 }
322 if (needLong == FALSE)
323 {
324 nbSlots = 1;
325 memcpy (Buffer, pEntry, sizeof (FATDirEntry));
326 memset (pEntry, 0, sizeof (FATDirEntry));
327 pEntry = (FATDirEntry *) Buffer;
328 }
329 else
330 {
331 memset (DirName, 0xff, sizeof (DirName));
332 memcpy (DirName, FileName, NameLen * sizeof (WCHAR));
333 DirName[NameLen] = 0;
334 }
335 DPRINT ("dos name=%11.11s\n", pEntry->Filename);
336
337 /* set attributes */
338 pEntry->Attrib = ReqAttr;
339 if (RequestedOptions & FILE_DIRECTORY_FILE)
340 pEntry->Attrib |= FILE_ATTRIBUTE_DIRECTORY;
341
342 /* set dates and times */
343 KeQuerySystemTime (&SystemTime);
344 ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
345 FsdFileTimeToDosDateTime ((TIME *) & LocalTime,
346 &pEntry->CreationDate, &pEntry->CreationTime);
347 pEntry->UpdateDate = pEntry->CreationDate;
348 pEntry->UpdateTime = pEntry->CreationTime;
349 pEntry->AccessDate = pEntry->CreationDate;
350
351 // calculate checksum for 8.3 name
352 for (pSlots[0].alias_checksum = i = 0; i < 11; i++)
353 {
354 pSlots[0].alias_checksum = (((pSlots[0].alias_checksum & 1) << 7
355 | ((pSlots[0].alias_checksum & 0xfe) >> 1))
356 + pEntry->Filename[i]);
357 }
358 //construct slots and entry
359 for (i = nbSlots - 2; i >= 0; i--)
360 {
361 DPRINT ("construct slot %d\n", i);
362 pSlots[i].attr = 0xf;
363 if (i)
364 pSlots[i].id = nbSlots - i - 1;
365 else
366 pSlots[i].id = nbSlots - i - 1 + 0x40;
367 pSlots[i].alias_checksum = pSlots[0].alias_checksum;
368 //FIXME pSlots[i].start=;
369 FillSlot (&pSlots[i], FileName + (nbSlots - i - 2) * 13);
370 }
371
372 //try to find nbSlots contiguous entries frees in directory
373 for (i = 0, status = STATUS_SUCCESS; status == STATUS_SUCCESS; i++)
374 {
375 status =
376 VfatReadFile (DeviceExt, pDirFcb->FileObject,
377 &FatEntry, sizeof (FATDirEntry),
378 i * sizeof (FATDirEntry), &LengthRead, FALSE);
379 if (status == STATUS_END_OF_FILE)
380 break;
381 if (!NT_SUCCESS (status))
382 {
383 DPRINT1 ("VfatReadFile failed to read the directory entry\n");
384 break;
385 }
386 if (LengthRead != sizeof (FATDirEntry))
387 {
388 DPRINT1 ("VfatReadFile did not read a complete directory entry\n");
389 break;
390 }
391 if (vfatIsDirEntryEndMarker(&FatEntry))
392 break;
393 if (vfatIsDirEntryDeleted(&FatEntry))
394 nbFree++;
395 else
396 nbFree = 0;
397
398 if (nbFree == nbSlots)
399 break;
400 }
401 DPRINT ("nbSlots %d nbFree %d, entry number %d\n", nbSlots, nbFree, i);
402
403 if (RequestedOptions & FILE_DIRECTORY_FILE)
404 {
405 CurrentCluster = 0xffffffff;
406 status = NextCluster (DeviceExt, NULL, 0, &CurrentCluster, TRUE);
407 if (CurrentCluster == 0xffffffff || !NT_SUCCESS(status))
408 {
409 vfatReleaseFCB(DeviceExt, pDirFcb);
410 ExFreePool (Buffer);
411 if (!NT_SUCCESS(status))
412 {
413 return status;
414 }
415 return STATUS_DISK_FULL;
416 }
417 // zero the cluster
418 Buffer2 = ExAllocatePool (NonPagedPool, DeviceExt->BytesPerCluster);
419 memset (Buffer2, 0, DeviceExt->BytesPerCluster);
420 VfatRawWriteCluster (DeviceExt, 0, Buffer2, CurrentCluster, 1);
421 ExFreePool (Buffer2);
422 if (DeviceExt->FatType == FAT32)
423 {
424 pEntry->FirstClusterHigh = CurrentCluster >> 16;
425 pEntry->FirstCluster = CurrentCluster;
426 }
427 else
428 pEntry->FirstCluster = CurrentCluster;
429 }
430 if (nbFree == nbSlots)
431 { //use old slots
432 Offset = (i - nbSlots + 1) * sizeof (FATDirEntry);
433 status =
434 VfatWriteFile (DeviceExt, pDirFcb->FileObject,
435 Buffer, sizeof (FATDirEntry) * nbSlots,
436 Offset, FALSE, FALSE);
437 DPRINT ("VfatWriteFile() returned: %x\n", status);
438 }
439 else
440 { //write at end of directory
441 Offset = (i - nbFree) * sizeof (FATDirEntry);
442 status =
443 VfatWriteFile (DeviceExt, pDirFcb->FileObject,
444 Buffer, sizeof (FATDirEntry) * (nbSlots + 1),
445 Offset, FALSE, FALSE);
446 }
447 DPRINT ("write entry offset %d status=%x\n", Offset, status);
448 if (!NT_SUCCESS(status))
449 {
450 vfatReleaseFCB (DeviceExt, pDirFcb);
451 if (RequestedOptions & FILE_DIRECTORY_FILE)
452 {
453 // free the reserved cluster
454 WriteCluster(DeviceExt, CurrentCluster, 0);
455 }
456 ExFreePool (Buffer);
457 return status;
458 }
459
460 // FEXME: check status
461 vfatMakeFCBFromDirEntry (DeviceExt, pDirFcb, FileName,
462 pEntry, Offset / sizeof(FATDirEntry) + nbSlots-1, &newFCB);
463 vfatAttachFCBToFileObject (DeviceExt, newFCB, pFileObject);
464
465 DPRINT ("new : entry=%11.11s\n", newFCB->entry.Filename);
466 DPRINT ("new : entry=%11.11s\n", pEntry->Filename);
467
468 if (RequestedOptions & FILE_DIRECTORY_FILE)
469 {
470 // create . and ..
471 memcpy (pEntry->Filename, ". ", 11);
472 status =
473 VfatWriteFile (DeviceExt, pFileObject, pEntry, sizeof (FATDirEntry),
474 0L, FALSE, FALSE);
475 pEntry->FirstCluster = pDirFcb->entry.FirstCluster;
476 pEntry->FirstClusterHigh = pDirFcb->entry.FirstClusterHigh;
477 memcpy (pEntry->Filename, ".. ", 11);
478 if (pEntry->FirstCluster == 1 && DeviceExt->FatType != FAT32)
479 pEntry->FirstCluster = 0;
480 status =
481 VfatWriteFile (DeviceExt, pFileObject, pEntry, sizeof (FATDirEntry),
482 sizeof (FATDirEntry), FALSE, FALSE);
483 }
484 vfatReleaseFCB (DeviceExt, pDirFcb);
485 ExFreePool (Buffer);
486 DPRINT ("addentry ok\n");
487 return STATUS_SUCCESS;
488 }
489
490 NTSTATUS
491 delEntry (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT pFileObject)
492 /*
493 deleting an existing FAT entry
494 */
495 {
496 VFATFCB Fcb;
497 PVFATFCB pFcb = NULL, pDirFcb = NULL;
498 NTSTATUS status;
499 PWSTR pName;
500 ULONG Entry = 0, startEntry, Read, CurrentCluster, NextCluster, i;
501 FATDirEntry DirEntry;
502
503 DPRINT ("delEntry PathFileName \'%S\'\n", pFileObject->FileName.Buffer);
504
505 status = vfatGetFCBForFile(DeviceExt, &pDirFcb, &pFcb, pFileObject->FileName.Buffer);
506 if (pFcb != NULL)
507 {
508 vfatReleaseFCB(DeviceExt, pFcb);
509 }
510 if (!NT_SUCCESS(status))
511 {
512 if (pDirFcb != NULL)
513 {
514 vfatReleaseFCB(DeviceExt, pDirFcb);
515 }
516 return status;
517 }
518 pName = ((PVFATCCB)(pFileObject->FsContext2))->pFcb->ObjectName;
519 if (*pName == L'\\')
520 {
521 pName ++;
522 }
523 status = FindFile (DeviceExt, &Fcb, pDirFcb, pName, &Entry, &startEntry);
524
525 if (NT_SUCCESS(status))
526 {
527 DPRINT ("delete entry: %d to %d\n", startEntry, Entry);
528 for (i = startEntry; i <= Entry; i++)
529 {
530 // FIXME: using Cc-functions
531 VfatReadFile (DeviceExt, pDirFcb->FileObject, &DirEntry,
532 sizeof (FATDirEntry), i * sizeof(FATDirEntry), &Read, FALSE);
533 DirEntry.Filename[0] = 0xe5;
534 // FIXME: check status
535 VfatWriteFile (DeviceExt, pDirFcb->FileObject, &DirEntry,
536 sizeof(FATDirEntry), i * sizeof(FATDirEntry), FALSE, FALSE);
537 }
538 CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &DirEntry);
539 while (CurrentCluster && CurrentCluster != 0xffffffff)
540 {
541 GetNextCluster (DeviceExt, CurrentCluster, &NextCluster, FALSE);
542 // FIXME: check status
543 WriteCluster(DeviceExt, CurrentCluster, 0);
544 CurrentCluster = NextCluster;
545 }
546 }
547 return status;
548 }
549
550 /* EOF */