1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
3 // All rights reserved
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 /*************************************************************************
7 *
8 * File: Sys_Spec.cpp
9 *
10 * Module: UDF File System Driver
11 * (both User and Kernel mode execution)
12 *
13 * Description:
14 *   Contains system-secific code
15 *
16 *************************************************************************/
17 
18 
19 /*
20     This routine converts UDF timestamp to NT time
21  */
22 LONGLONG
23 UDFTimeToNT(
24     IN PUDF_TIME_STAMP UdfTime
25     )
26 {
27     LONGLONG NtTime;
28     TIME_FIELDS TimeFields;
29 
30     TimeFields.Milliseconds = (USHORT)(UdfTime->centiseconds * 10 + UdfTime->hundredsOfMicroseconds / 100);
31     TimeFields.Second = (USHORT)(UdfTime->second);
32     TimeFields.Minute = (USHORT)(UdfTime->minute);
33     TimeFields.Hour = (USHORT)(UdfTime->hour);
34     TimeFields.Day = (USHORT)(UdfTime->day);
35     TimeFields.Month = (USHORT)(UdfTime->month);
36     TimeFields.Year = (USHORT)((UdfTime->year < 1601) ? 1601 : UdfTime->year);
37 
38     if (!RtlTimeFieldsToTime(&TimeFields, (PLARGE_INTEGER)&NtTime)) {
39         NtTime = 0;
40     } else {
41         ExLocalTimeToSystemTime( (PLARGE_INTEGER)&NtTime, (PLARGE_INTEGER)&NtTime );
42     }
43 
44     return NtTime;
45 } // end UDFTimeToNT()
46 
47 
48 /*
49     This routine converts NT time to UDF timestamp
50  */
51 VOID
52 UDFTimeToUDF(
53     IN LONGLONG NtTime,
54     OUT PUDF_TIME_STAMP UdfTime
55     )
56 {
57     if(!NtTime) return;
58     LONGLONG LocalTime;
59 
60     TIME_FIELDS TimeFields;
61 
62     ExSystemTimeToLocalTime( (PLARGE_INTEGER)&NtTime, (PLARGE_INTEGER)&LocalTime );
63     RtlTimeToTimeFields( (PLARGE_INTEGER)&LocalTime, &TimeFields );
64 
65     LocalTime /= 10; // microseconds
66     UdfTime->microseconds = (UCHAR)(NtTime % 100);
67     LocalTime /= 100; // hundreds of microseconds
68     UdfTime->hundredsOfMicroseconds = (UCHAR)(NtTime % 100);
69     LocalTime /= 100; // centiseconds
70     UdfTime->centiseconds = (UCHAR)(TimeFields.Milliseconds / 10);
71     UdfTime->second = (UCHAR)(TimeFields.Second);
72     UdfTime->minute = (UCHAR)(TimeFields.Minute);
73     UdfTime->hour = (UCHAR)(TimeFields.Hour);
74     UdfTime->day = (UCHAR)(TimeFields.Day);
75     UdfTime->month = (UCHAR)(TimeFields.Month);
76     UdfTime->year = (USHORT)(TimeFields.Year);
77     UdfTime->typeAndTimezone = (TIMESTAMP_TYPE_LOCAL << 14);
78 } // end UDFTimeToUDF()
79 
80 /*
81  */
82 ULONG
83 UDFAttributesToNT(
84     IN PDIR_INDEX_ITEM FileDirNdx,
85     IN tag* FileEntry
86     )
87 {
88     ASSERT(FileDirNdx);
89     if( (FileDirNdx->FI_Flags & UDF_FI_FLAG_SYS_ATTR) &&
90        !(FileDirNdx->FI_Flags & UDF_FI_FLAG_LINKED))
91         return FileDirNdx->SysAttr;
92 
93     ULONG NTAttr = 0;
94     ULONG attr = 0; //permissions
95     USHORT Flags = 0;
96     USHORT Type = 0;
97     UCHAR FCharact = 0;
98 
99     if(!FileEntry) {
100         if(!FileDirNdx->FileInfo)
101             return 0;
102         ValidateFileInfo(FileDirNdx->FileInfo);
103         FileEntry = FileDirNdx->FileInfo->Dloc->FileEntry;
104     }
105     if(FileEntry->tagIdent == TID_FILE_ENTRY) {
106         attr = ((PFILE_ENTRY)FileEntry)->permissions;
107         Flags = ((PFILE_ENTRY)FileEntry)->icbTag.flags;
108         Type = ((PFILE_ENTRY)FileEntry)->icbTag.fileType;
109         if(((PFILE_ENTRY)FileEntry)->fileLinkCount > 1)
110             FileDirNdx->FI_Flags |= UDF_FI_FLAG_LINKED;
111     } else {
112         attr = ((PEXTENDED_FILE_ENTRY)FileEntry)->permissions;
113         Flags = ((PEXTENDED_FILE_ENTRY)FileEntry)->icbTag.flags;
114         Type = ((PEXTENDED_FILE_ENTRY)FileEntry)->icbTag.fileType;
115         if(((PEXTENDED_FILE_ENTRY)FileEntry)->fileLinkCount > 1)
116             FileDirNdx->FI_Flags |= UDF_FI_FLAG_LINKED;
117     }
118     FCharact = FileDirNdx->FileCharacteristics;
119 
120     if(Flags & ICB_FLAG_SYSTEM) NTAttr |= FILE_ATTRIBUTE_SYSTEM;
121     if(Flags & ICB_FLAG_ARCHIVE) NTAttr |= FILE_ATTRIBUTE_ARCHIVE;
122     if((Type == UDF_FILE_TYPE_DIRECTORY) ||
123        (Type == UDF_FILE_TYPE_STREAMDIR) ||
124        (FCharact & FILE_DIRECTORY)) {
125         NTAttr |= FILE_ATTRIBUTE_DIRECTORY;
126 #ifdef UDF_DBG
127     } else {
128         //NTAttr |= FILE_ATTRIBUTE_NORMAL;
129 #endif
130     }
131     if(FCharact & FILE_HIDDEN) NTAttr |= FILE_ATTRIBUTE_HIDDEN;
132     if( !(attr & PERM_O_WRITE) &&
133         !(attr & PERM_G_WRITE) &&
134         !(attr & PERM_U_WRITE) &&
135         !(attr & PERM_O_DELETE) &&
136         !(attr & PERM_G_DELETE) &&
137         !(attr & PERM_U_DELETE) ) {
138         NTAttr |= FILE_ATTRIBUTE_READONLY;
139     }
140     FileDirNdx->SysAttr = NTAttr;
141     return NTAttr;
142 } // end UDFAttributesToNT()
143 
144 /*
145  */
146 VOID
147 UDFAttributesToUDF(
148     IN PDIR_INDEX_ITEM FileDirNdx,
149     IN tag* FileEntry,
150     IN ULONG NTAttr
151     )
152 {
153     PULONG attr; //permissions
154     PUSHORT Flags;
155     PUCHAR Type;
156     PUCHAR FCharact;
157 
158     NTAttr &= UDF_VALID_FILE_ATTRIBUTES;
159 
160     if(!FileEntry) {
161         ASSERT(FileDirNdx);
162         if(!FileDirNdx->FileInfo)
163             return;
164         ValidateFileInfo(FileDirNdx->FileInfo);
165         FileEntry = FileDirNdx->FileInfo->Dloc->FileEntry;
166         FileDirNdx->FileInfo->Dloc->FE_Flags |= UDF_FE_FLAG_FE_MODIFIED;
167     }
168     if(FileEntry->tagIdent == TID_FILE_ENTRY) {
169         attr = &((PFILE_ENTRY)FileEntry)->permissions;
170         Flags = &((PFILE_ENTRY)FileEntry)->icbTag.flags;
171         Type = &((PFILE_ENTRY)FileEntry)->icbTag.fileType;
172     } else {
173         attr = &((PEXTENDED_FILE_ENTRY)FileEntry)->permissions;
174         Flags = &((PEXTENDED_FILE_ENTRY)FileEntry)->icbTag.flags;
175         Type = &((PEXTENDED_FILE_ENTRY)FileEntry)->icbTag.fileType;
176     }
177     FCharact = &(FileDirNdx->FileCharacteristics);
178 
179     if((*FCharact & FILE_DIRECTORY) ||
180        (*Type == UDF_FILE_TYPE_STREAMDIR) ||
181        (*Type == UDF_FILE_TYPE_DIRECTORY)) {
182         *FCharact |= FILE_DIRECTORY;
183         if(*Type != UDF_FILE_TYPE_STREAMDIR)
184             *Type = UDF_FILE_TYPE_DIRECTORY;
185         *attr |= (PERM_O_EXEC | PERM_G_EXEC | PERM_U_EXEC);
186         NTAttr |= FILE_ATTRIBUTE_DIRECTORY;
187         NTAttr &= ~FILE_ATTRIBUTE_NORMAL;
188     } else {
189         *FCharact &= ~FILE_DIRECTORY;
190         *Type = UDF_FILE_TYPE_REGULAR;
191         *attr &= ~(PERM_O_EXEC | PERM_G_EXEC | PERM_U_EXEC);
192     }
193 
194     if(NTAttr & FILE_ATTRIBUTE_SYSTEM) {
195         *Flags |= ICB_FLAG_SYSTEM;
196     } else {
197         *Flags &= ~ICB_FLAG_SYSTEM;
198     }
199     if(NTAttr & FILE_ATTRIBUTE_ARCHIVE) {
200         *Flags |= ICB_FLAG_ARCHIVE;
201     } else {
202         *Flags &= ~ICB_FLAG_ARCHIVE;
203     }
204     if(NTAttr & FILE_ATTRIBUTE_HIDDEN) {
205        *FCharact |= FILE_HIDDEN;
206     } else {
207        *FCharact &= ~FILE_HIDDEN;
208     }
209     *attr |= (PERM_O_READ | PERM_G_READ | PERM_U_READ);
210     if(!(NTAttr & FILE_ATTRIBUTE_READONLY)) {
211         *attr |= (PERM_O_WRITE  | PERM_G_WRITE  | PERM_U_WRITE |
212                   PERM_O_DELETE | PERM_G_DELETE | PERM_U_DELETE |
213                   PERM_O_CHATTR | PERM_G_CHATTR | PERM_U_CHATTR);
214     } else {
215         *attr &= ~(PERM_O_WRITE  | PERM_G_WRITE  | PERM_U_WRITE |
216                    PERM_O_DELETE | PERM_G_DELETE | PERM_U_DELETE |
217                    PERM_O_CHATTR | PERM_G_CHATTR | PERM_U_CHATTR);
218     }
219     FileDirNdx->SysAttr = NTAttr;
220     if(FileDirNdx->FileInfo)
221         FileDirNdx->FileInfo->Dloc->FE_Flags |= UDF_FE_FLAG_FE_MODIFIED;
222     FileDirNdx->FI_Flags |= UDF_FI_FLAG_FI_MODIFIED;
223     return;
224 } // end UDFAttributesToUDF()
225 
226 #ifndef _CONSOLE
227 /*
228     This routine fills PFILE_BOTH_DIR_INFORMATION structure (NT)
229  */
230 NTSTATUS
231 UDFFileDirInfoToNT(
232     IN PVCB Vcb,
233     IN PDIR_INDEX_ITEM FileDirNdx,
234     OUT PFILE_BOTH_DIR_INFORMATION NTFileInfo
235     )
236 {
237     PFILE_ENTRY FileEntry;
238     UNICODE_STRING UdfName;
239     UNICODE_STRING DosName;
240     PEXTENDED_FILE_ENTRY ExFileEntry;
241     USHORT Ident;
242     BOOLEAN ReadSizes = FALSE;
243     NTSTATUS status;
244     PtrUDFNTRequiredFCB NtReqFcb;
245 
246     UDFPrint(("@=%#x, FileDirNdx %x\n", &Vcb, FileDirNdx));
247 
248     ASSERT((ULONG_PTR)NTFileInfo > 0x1000);
249     RtlZeroMemory(NTFileInfo, sizeof(FILE_BOTH_DIR_INFORMATION));
250 
251     DosName.Buffer = (PWCHAR)&(NTFileInfo->ShortName);
252     DosName.MaximumLength = sizeof(NTFileInfo->ShortName); // 12*sizeof(WCHAR)
253 
254     _SEH2_TRY {
255         UDFPrint(("  DirInfoToNT: %*.*S\n", FileDirNdx->FName.Length/sizeof(WCHAR), FileDirNdx->FName.Length/sizeof(WCHAR), FileDirNdx->FName));
256     } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
257         UDFPrint(("  DirInfoToNT: exception when printing file name\n"));
258     } _SEH2_END;
259 
260     if(FileDirNdx->FileInfo) {
261         UDFPrint(("    FileInfo\n"));
262         // validate FileInfo
263         ValidateFileInfo(FileDirNdx->FileInfo);
264         if(UDFGetFileLinkCount(FileDirNdx->FileInfo) > 1)
265             FileDirNdx->FI_Flags |= UDF_FI_FLAG_LINKED;
266         FileEntry = (PFILE_ENTRY)(FileDirNdx->FileInfo->Dloc->FileEntry);
267         // read required sizes from Fcb (if any) if file is not linked
268         // otherwise we should read them from FileEntry
269         if(FileDirNdx->FileInfo->Fcb) {
270             UDFPrint(("    Fcb\n"));
271             NtReqFcb = FileDirNdx->FileInfo->Fcb->NTRequiredFCB;
272             NTFileInfo->CreationTime.QuadPart   = NtReqFcb->CreationTime.QuadPart;
273             NTFileInfo->LastWriteTime.QuadPart  = NtReqFcb->LastWriteTime.QuadPart;
274             NTFileInfo->LastAccessTime.QuadPart = NtReqFcb->LastAccessTime.QuadPart;
275             NTFileInfo->ChangeTime.QuadPart     = NtReqFcb->ChangeTime.QuadPart;
276 //            NTFileInfo->AllocationSize.QuadPart = NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart;
277             NTFileInfo->AllocationSize.QuadPart = FileDirNdx->AllocationSize;
278 /*            FileDirNdx->FileSize =
279             NTFileInfo->EndOfFile.QuadPart = NtReqFcb->CommonFCBHeader.FileSize.QuadPart;*/
280             NTFileInfo->EndOfFile.QuadPart = FileDirNdx->FileSize;
281             if(FileDirNdx->FI_Flags & UDF_FI_FLAG_SYS_ATTR) {
282                 UDFPrint(("    SYS_ATTR\n"));
283                 NTFileInfo->FileAttributes = FileDirNdx->SysAttr;
284                 goto get_name_only;
285             }
286             FileDirNdx->CreationTime   = NTFileInfo->CreationTime.QuadPart;
287             FileDirNdx->LastWriteTime  = NTFileInfo->LastWriteTime.QuadPart;
288             FileDirNdx->LastAccessTime = NTFileInfo->LastAccessTime.QuadPart;
289             FileDirNdx->ChangeTime     = NTFileInfo->ChangeTime.QuadPart;
290             goto get_attr_only;
291         }
292         ASSERT(FileEntry);
293     } else if(!(FileDirNdx->FI_Flags & UDF_FI_FLAG_SYS_ATTR) ||
294                (FileDirNdx->FI_Flags & UDF_FI_FLAG_LINKED)) {
295         LONG_AD feloc;
296 
297         UDFPrint(("  !SYS_ATTR\n"));
298         FileEntry = (PFILE_ENTRY)MyAllocatePool__(NonPagedPool, Vcb->LBlockSize);
299         if(!FileEntry) return STATUS_INSUFFICIENT_RESOURCES;
300 
301         feloc.extLength = Vcb->LBlockSize;
302         feloc.extLocation = FileDirNdx->FileEntryLoc;
303 
304         if(!NT_SUCCESS(status = UDFReadFileEntry(Vcb, &feloc, FileEntry, &Ident))) {
305             UDFPrint(("    !UDFReadFileEntry\n"));
306             MyFreePool__(FileEntry);
307             FileEntry = NULL;
308             goto get_name_only;
309         }
310         ReadSizes = TRUE;
311     } else {
312         UDFPrint(("  FileDirNdx\n"));
313         NTFileInfo->CreationTime.QuadPart   = FileDirNdx->CreationTime;
314         NTFileInfo->LastWriteTime.QuadPart  = FileDirNdx->LastWriteTime;
315         NTFileInfo->LastAccessTime.QuadPart = FileDirNdx->LastAccessTime;
316         NTFileInfo->ChangeTime.QuadPart     = FileDirNdx->ChangeTime;
317         NTFileInfo->FileAttributes = FileDirNdx->SysAttr;
318         NTFileInfo->AllocationSize.QuadPart = FileDirNdx->AllocationSize;
319         NTFileInfo->EndOfFile.QuadPart = FileDirNdx->FileSize;
320         NTFileInfo->EaSize = 0;
321         FileEntry = NULL;
322         goto get_name_only;
323     }
324 
325     if(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)
326         goto get_name_only;
327 
328     UDFPrint(("  direct\n"));
329     if(FileEntry->descTag.tagIdent == TID_FILE_ENTRY) {
330         UDFPrint(("  TID_FILE_ENTRY\n"));
331         if(ReadSizes) {
332             UDFPrint(("    ReadSizes\n"));
333             // Times
334             FileDirNdx->CreationTime   = NTFileInfo->CreationTime.QuadPart   =
335             FileDirNdx->LastWriteTime  = NTFileInfo->LastWriteTime.QuadPart  = UDFTimeToNT(&(FileEntry->modificationTime));
336             FileDirNdx->LastAccessTime = NTFileInfo->LastAccessTime.QuadPart = UDFTimeToNT(&(FileEntry->accessTime));
337             FileDirNdx->ChangeTime     = NTFileInfo->ChangeTime.QuadPart     = UDFTimeToNT(&(FileEntry->attrTime));
338             // FileSize
339             FileDirNdx->FileSize =
340             NTFileInfo->EndOfFile.QuadPart =
341                 FileEntry->informationLength;
342             UDFPrint(("    informationLength=%I64x, lengthAllocDescs=%I64x\n",
343                 FileEntry->informationLength,
344                 FileEntry->lengthAllocDescs
345                 ));
346             // AllocSize
347             FileDirNdx->AllocationSize =
348             NTFileInfo->AllocationSize.QuadPart =
349                 (FileEntry->informationLength + Vcb->LBlockSize - 1) & ~((LONGLONG)(Vcb->LBlockSize) - 1);
350         }
351 //        NTFileInfo->EaSize = 0;//FileEntry->lengthExtendedAttr;
352     } else if(FileEntry->descTag.tagIdent == TID_EXTENDED_FILE_ENTRY) {
353         ExFileEntry = (PEXTENDED_FILE_ENTRY)FileEntry;
354         UDFPrint(("  PEXTENDED_FILE_ENTRY\n"));
355         if(ReadSizes) {
356             UDFPrint(("    ReadSizes\n"));
357             // Times
358             FileDirNdx->CreationTime   = NTFileInfo->CreationTime.QuadPart   = UDFTimeToNT(&(ExFileEntry->createTime));
359             FileDirNdx->LastWriteTime  = NTFileInfo->LastWriteTime.QuadPart  = UDFTimeToNT(&(ExFileEntry->modificationTime));
360             FileDirNdx->LastAccessTime = NTFileInfo->LastAccessTime.QuadPart = UDFTimeToNT(&(ExFileEntry->accessTime));
361             FileDirNdx->ChangeTime     = NTFileInfo->ChangeTime.QuadPart     = UDFTimeToNT(&(ExFileEntry->attrTime));
362             // FileSize
363             FileDirNdx->FileSize =
364             NTFileInfo->EndOfFile.QuadPart =
365                 ExFileEntry->informationLength;
366             UDFPrint(("    informationLength=%I64x, lengthAllocDescs=%I64x\n",
367                 FileEntry->informationLength,
368                 FileEntry->lengthAllocDescs
369                 ));
370             // AllocSize
371             FileDirNdx->AllocationSize =
372             NTFileInfo->AllocationSize.QuadPart =
373                 (ExFileEntry->informationLength + Vcb->LBlockSize - 1) & ~((LONGLONG)(Vcb->LBlockSize) - 1);
374         }
375 //        NTFileInfo->EaSize = 0;//ExFileEntry->lengthExtendedAttr;
376     } else {
377         UDFPrint(("  ???\n"));
378         goto get_name_only;
379     }
380 
381 get_attr_only:
382 
383     UDFPrint(("  get_attr"));
384     // do some substitutions
385     if(!FileDirNdx->CreationTime) {
386         FileDirNdx->CreationTime = NTFileInfo->CreationTime.QuadPart = Vcb->VolCreationTime;
387     }
388     if(!FileDirNdx->LastAccessTime) {
389         FileDirNdx->LastAccessTime = NTFileInfo->LastAccessTime.QuadPart = FileDirNdx->CreationTime;
390     }
391     if(!FileDirNdx->LastWriteTime) {
392         FileDirNdx->LastWriteTime = NTFileInfo->LastWriteTime.QuadPart = FileDirNdx->CreationTime;
393     }
394     if(!FileDirNdx->ChangeTime) {
395         FileDirNdx->ChangeTime = NTFileInfo->ChangeTime.QuadPart = FileDirNdx->CreationTime;
396     }
397 
398     FileDirNdx->SysAttr =
399     NTFileInfo->FileAttributes = UDFAttributesToNT(FileDirNdx, (tag*)FileEntry);
400     FileDirNdx->FI_Flags |= UDF_FI_FLAG_SYS_ATTR;
401 
402 get_name_only:
403     // get filename in standard Unicode format
404     UdfName = FileDirNdx->FName;
405     NTFileInfo->FileNameLength = UdfName.Length;
406     RtlCopyMemory((PCHAR)&(NTFileInfo->FileName), (PCHAR)(UdfName.Buffer), UdfName.MaximumLength);
407     if(!(FileDirNdx->FI_Flags & UDF_FI_FLAG_DOS)) {
408         UDFPrint(("  !UDF_FI_FLAG_DOS"));
409         UDFDOSName(Vcb, &DosName, &UdfName,
410             (FileDirNdx->FI_Flags & UDF_FI_FLAG_KEEP_NAME) ? TRUE : FALSE);
411         NTFileInfo->ShortNameLength = (UCHAR)DosName.Length;
412     }
413     // report zero EOF & AllocSize for Dirs
414     if(FileDirNdx->FileCharacteristics & FILE_DIRECTORY) {
415         UDFPrint(("  FILE_DIRECTORY"));
416         NTFileInfo->AllocationSize.QuadPart =
417         NTFileInfo->EndOfFile.QuadPart = 0;
418     }
419     UDFPrint(("  AllocationSize=%I64x, NTFileInfo->EndOfFile=%I64x", NTFileInfo->AllocationSize.QuadPart, NTFileInfo->EndOfFile.QuadPart));
420     // free tmp buffer (if any)
421     UDFPrint(("\n"));
422     if(FileEntry && !FileDirNdx->FileInfo)
423         MyFreePool__(FileEntry);
424     return STATUS_SUCCESS;
425 } // end UDFFileDirInfoToNT()
426 
427 #endif //_CONSOLE
428 
429 #ifndef UDF_READ_ONLY_BUILD
430 /*
431     This routine changes xxxTime field(s) in (Ext)FileEntry
432  */
433 VOID
434 UDFSetFileXTime(
435     IN PUDF_FILE_INFO FileInfo,
436     IN LONGLONG* CrtTime,
437     IN LONGLONG* AccTime,
438     IN LONGLONG* AttrTime,
439     IN LONGLONG* ChgTime
440     )
441 {
442     USHORT Ident;
443     PDIR_INDEX_ITEM DirNdx;
444 
445     ValidateFileInfo(FileInfo);
446 
447     FileInfo->Dloc->FE_Flags |= UDF_FE_FLAG_FE_MODIFIED;
448     DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(FileInfo), FileInfo->Index);
449     Ident = FileInfo->Dloc->FileEntry->tagIdent;
450 
451     if(Ident == TID_FILE_ENTRY) {
452         PFILE_ENTRY fe = (PFILE_ENTRY)(FileInfo->Dloc->FileEntry);
453 
454         if(AccTime) {
455             if(DirNdx && *AccTime) DirNdx->LastAccessTime = *AccTime;
456             UDFTimeToUDF(*AccTime, &(fe->accessTime));
457         }
458         if(AttrTime) {
459             if(DirNdx && *AttrTime) DirNdx->ChangeTime = *AttrTime;
460             UDFTimeToUDF(*AttrTime, &(fe->attrTime));
461         }
462         if(ChgTime) {
463             if(DirNdx && *ChgTime) DirNdx->CreationTime =
464                 DirNdx->LastWriteTime = *ChgTime;
465             UDFTimeToUDF(*ChgTime, &(fe->modificationTime));
466         } else
467         if(CrtTime) {
468             if(DirNdx && *CrtTime) DirNdx->CreationTime =
469                 DirNdx->LastWriteTime = *CrtTime;
470             UDFTimeToUDF(*CrtTime, &(fe->modificationTime));
471         }
472 
473     } else if(Ident == TID_EXTENDED_FILE_ENTRY) {
474         PEXTENDED_FILE_ENTRY fe = (PEXTENDED_FILE_ENTRY)(FileInfo->Dloc->FileEntry);
475 
476         if(AccTime) {
477             if(DirNdx && *AccTime) DirNdx->LastAccessTime = *AccTime;
478             UDFTimeToUDF(*AccTime, &(fe->accessTime));
479         }
480         if(AttrTime) {
481             if(DirNdx && *AttrTime) DirNdx->ChangeTime = *AttrTime;
482             UDFTimeToUDF(*AttrTime, &(fe->attrTime));
483         }
484         if(ChgTime) {
485             if(DirNdx && *ChgTime) DirNdx->LastWriteTime = *ChgTime;
486             UDFTimeToUDF(*ChgTime, &(fe->modificationTime));
487         }
488         if(CrtTime) {
489             if(DirNdx && *CrtTime) DirNdx->CreationTime = *CrtTime;
490             UDFTimeToUDF(*CrtTime, &(fe->createTime));
491         }
492 
493     }
494 } // end UDFSetFileXTime()
495 #endif //UDF_READ_ONLY_BUILD
496 
497 /*
498     This routine gets xxxTime field(s) in (Ext)FileEntry
499  */
500 VOID
501 UDFGetFileXTime(
502     IN PUDF_FILE_INFO FileInfo,
503     OUT LONGLONG* CrtTime,
504     OUT LONGLONG* AccTime,
505     OUT LONGLONG* AttrTime,
506     OUT LONGLONG* ChgTime
507     )
508 {
509     USHORT Ident;
510 
511     ValidateFileInfo(FileInfo);
512 
513     Ident = FileInfo->Dloc->FileEntry->tagIdent;
514 
515     if(Ident == TID_FILE_ENTRY) {
516         PFILE_ENTRY fe = (PFILE_ENTRY)(FileInfo->Dloc->FileEntry);
517 
518         if(AccTime) *AccTime = UDFTimeToNT(&(fe->accessTime));
519         if(AttrTime) *AttrTime = UDFTimeToNT(&(fe->attrTime));
520         if(ChgTime) *ChgTime = UDFTimeToNT(&(fe->modificationTime));
521         if(CrtTime) {
522             (*CrtTime) = *ChgTime;
523         }
524 
525     } else if(Ident == TID_EXTENDED_FILE_ENTRY) {
526         PEXTENDED_FILE_ENTRY fe = (PEXTENDED_FILE_ENTRY)(FileInfo->Dloc->FileEntry);
527 
528         if(AccTime) *AccTime = UDFTimeToNT(&(fe->accessTime));
529         if(AttrTime) *AttrTime = UDFTimeToNT(&(fe->attrTime));
530         if(ChgTime) *ChgTime = UDFTimeToNT(&(fe->modificationTime));
531         if(CrtTime) *CrtTime = UDFTimeToNT(&(fe->createTime));
532 
533     }
534     if(CrtTime) {
535         if(!(*CrtTime))
536             KeQuerySystemTime((PLARGE_INTEGER)CrtTime);
537         if(AccTime && !(*AccTime)) (*AccTime) = *CrtTime;
538         if(AttrTime && !(*AttrTime)) (*AttrTime) = *CrtTime;
539         if(AccTime && !(*AccTime)) (*AccTime) = *CrtTime;
540     }
541 } // end UDFGetFileXTime()
542 
543 VOID
544 UDFNormalizeFileName(
545     IN PUNICODE_STRING FName,
546     IN USHORT valueCRC
547     )
548 {
549     PWCHAR buffer;
550     USHORT len;
551 
552     len = FName->Length/sizeof(WCHAR);
553     buffer = FName->Buffer;
554 
555     // check for '',  '.'  &  '..'
556     if(!len) return;
557     if(!buffer[len-1]) {
558         FName->Length-=sizeof(WCHAR);
559         len--;
560     }
561     if(!len) return;
562     if(buffer[0] == UNICODE_PERIOD) {
563         if(len == 1) return;
564         if((buffer[1] == UNICODE_PERIOD) && (len == 2)) return;
565     }
566 
567     // check for trailing '.'
568     for(len--;len;len--) {
569         if( ((buffer[len] == UNICODE_PERIOD) || (buffer[len] == UNICODE_SPACE)) ) {
570             FName->Length-=sizeof(WCHAR);
571             buffer[len] = 0;
572         } else
573             break;
574     }
575 } // end UDFNormalizeFileName()
576 
577 #ifndef _CONSOLE
578 
579 void
580 __fastcall
581 UDFDOSNameOsNative(
582     IN OUT PUNICODE_STRING DosName,
583     IN PUNICODE_STRING UdfName,
584     IN BOOLEAN KeepIntact
585     )
586 {
587     PWCHAR dosName = DosName->Buffer;
588     PWCHAR udfName = UdfName->Buffer;
589     uint32 udfLen = UdfName->Length / sizeof(WCHAR);
590     GENERATE_NAME_CONTEXT Ctx;
591 
592     if(KeepIntact &&
593        (udfLen <= 2) && (udfName[0] == UNICODE_PERIOD)) {
594         if((udfLen != 2) || (udfName[1] == UNICODE_PERIOD)) {
595             RtlCopyMemory(dosName, udfName, UdfName->Length);
596             DosName->Length = UdfName->Length;
597             return;
598         }
599     }
600     RtlZeroMemory(&Ctx, sizeof(GENERATE_NAME_CONTEXT));
601     RtlGenerate8dot3Name(UdfName, FALSE, &Ctx, DosName);
602 
603 } // UDFDOSNameOsNative()
604 
605 #endif //_CONSOLE
606 
607 /*VOID
608 UDFNormalizeFileName(
609     IN PUNICODE_STRING FName,
610     IN USHORT valueCRC
611     )
612 {
613     WCHAR _newName[UDF_NAME_LEN+5];
614     PWCHAR newName = (PWCHAR)(&_newName);
615     PWCHAR udfName = FName->Buffer;
616     LONG udfLen = FName->Length >> 1;
617 
618     LONG index, newIndex = 0, extIndex = 0, newExtIndex = 0, trailIndex = 0;
619     BOOLEAN needsCRC = FALSE, hasExt = FALSE;
620     WCHAR ext[UDF_EXT_SIZE], current;
621 
622     // handle CurrentDir ('.') and ParentDir ('..') cases
623     if((udfLen <= 2) && (udfName[0] == UNICODE_PERIOD)) {
624         if((udfLen != 2) || (udfName[1] == UNICODE_PERIOD))
625             return;
626     }
627 
628     for (index = 0 ; index < udfLen ; index++) {
629         current = udfName[index];
630 
631         // Look for illegal or unprintable characters.
632         if (UDFIsIllegalChar(current) || !UnicodeIsPrint(current)) {
633             needsCRC = TRUE;
634             current = ILLEGAL_CHAR_MARK;
635             // Skip Illegal characters(even spaces),
636             // but not periods.
637             while(index+1 < udfLen &&
638                   (UDFIsIllegalChar(udfName[index+1]) ||
639                   !UnicodeIsPrint(udfName[index+1])) &&
640                   udfName[index+1] != UNICODE_PERIOD)
641                 index++;
642         }
643 
644         // Record position of extension, if one is found.
645         if ((current == UNICODE_PERIOD) && ((udfLen - index -1) <= UDF_EXT_SIZE)) {
646             if (udfLen == index + 1) {
647                 // A trailing period is NOT an extension.
648                 hasExt = FALSE;
649             } else {
650                 hasExt = TRUE;
651                 extIndex = index;
652                 newExtIndex = newIndex;
653             }
654         } else if((current != UNICODE_PERIOD) && (current != UNICODE_SPACE)) {
655             trailIndex = index;
656         }
657 
658 //        if (newIndex < MAXLEN)  // tshi is always TRUE for WINNT
659         newName[newIndex] = current;
660         newIndex++;
661 
662         // For OS2, 95 & NT, truncate any trailing periods and\or spaces.
663         if (trailIndex != (newIndex - 1)) {
664             newIndex = trailIndex + 1;
665             needsCRC = TRUE;
666             hasExt = FALSE; // Trailing period does not make an extension.
667         }
668     }
669 
670     if (needsCRC) {
671         int localExtIndex = 0;
672         if (hasExt) {
673             int maxFilenameLen;
674             // Translate extension, and store it in ext.
675             for(index = 0; index<UDF_EXT_SIZE && extIndex + index +1 < udfLen; index++ ) {
676                 current = udfName[extIndex + index + 1];
677                 if (UDFIsIllegalChar(current) //|| !UnicodeIsPrint(current)) {
678                     needsCRC = TRUE;
679                     // Replace Illegal and non-displayable chars
680                     // with underscore.
681                     current = ILLEGAL_CHAR_MARK;
682                     // Skip any other illegal or non-displayable
683                     // characters.
684                     while(index + 1 < UDF_EXT_SIZE &&
685                           (UDFIsIllegalChar(udfName[extIndex + index + 2]) ||
686                           !UnicodeIsPrint(udfName[extIndex + index + 2])) )
687                         index++;
688                 }
689                 ext[localExtIndex++] = current;
690             }
691             // Truncate filename to leave room for extension and CRC.
692             maxFilenameLen = ((UDF_NAME_LEN - 4) - localExtIndex - 1);
693             if (newIndex > maxFilenameLen) {
694                 newIndex = maxFilenameLen;
695             } else {
696                 newIndex = newExtIndex;
697             }
698         } else if (newIndex > UDF_NAME_LEN - 5) {
699             //If no extension, make sure to leave room for CRC.
700             newIndex = UDF_NAME_LEN - 5;
701         }
702         newName[newIndex++] = UNICODE_CRC_MARK; // Add mark for CRC.
703         //Calculate CRC from original filename from FileIdentifier.
704 //        valueCRC = UDFUnicodeCksum(fidName, fidNameLen);
705 //        / Convert 16-bits of CRC to hex characters.
706         newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12];
707         newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8];
708         newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4];
709         newName[newIndex++] = hexChar[(valueCRC & 0x000f)];
710         // Place a translated extension at end, if found.
711         if (hasExt) {
712             newName[newIndex++] = UNICODE_PERIOD;
713             for (index = 0;index < localExtIndex ;index++ ) {
714                 newName[newIndex++] = ext[index];
715             }
716         }
717     }
718 
719     if(FName->Length == (USHORT)newIndex*sizeof(WCHAR)) {
720         RtlCopyMemory(FName->Buffer, newName, newIndex*sizeof(WCHAR));
721         return;
722     }
723     MyFreePool__(FName->Buffer);
724     FName->Buffer = (PWCHAR)MyAllocatePool__(UDF_FILENAME_MT, (newIndex+1)*sizeof(WCHAR));
725     if(FName->Buffer) {
726         FName->Buffer[newIndex] = 0;
727         RtlCopyMemory(FName->Buffer, newName, newIndex*sizeof(WCHAR));
728     }
729     FName->Length = (USHORT)newIndex*sizeof(WCHAR);
730     FName->MaximumLength = (USHORT)(newIndex+1)*sizeof(WCHAR);
731 }*/
732 
733 /*PUDF_FILE_INFO
734 UDFAllocFileInfo(
735     return ExAllocateFromZone(&(UDFGlobalData.FileInfoZoneHeader));
736 )*/
737 
738 #define STRING_BUFFER_ALIGNMENT  (32)
739 #define STRING_BUFFER_ALIGN(sz)  (((sz)+STRING_BUFFER_ALIGNMENT)&(~((ULONG)(STRING_BUFFER_ALIGNMENT-1))))
740 
741 NTSTATUS
742 MyAppendUnicodeStringToString_(
743     IN PUNICODE_STRING Str1,
744     IN PUNICODE_STRING Str2
745 #ifdef UDF_TRACK_UNICODE_STR
746    ,IN PCHAR Tag
747 #endif
748     )
749 {
750     PWCHAR tmp;
751     USHORT i;
752 
753 #ifdef UDF_TRACK_UNICODE_STR
754   #define UDF_UNC_STR_TAG Tag
755 #else
756   #define UDF_UNC_STR_TAG "AppUStr"
757 #endif
758 
759     tmp = Str1->Buffer;
760     i = Str1->Length + Str2->Length + sizeof(WCHAR);
761     ASSERT(Str1->MaximumLength);
762     if(i > Str1->MaximumLength) {
763         if(!MyReallocPool__((PCHAR)tmp, Str1->MaximumLength,
764                          (PCHAR*)&tmp, STRING_BUFFER_ALIGN(i)*2) ) {
765             return STATUS_INSUFFICIENT_RESOURCES;
766         }
767         Str1->MaximumLength = i*2;
768         Str1->Buffer = tmp;
769     }
770     RtlCopyMemory(((PCHAR)tmp)+Str1->Length, Str2->Buffer, Str2->Length);
771 
772 /*    tmp = (PWCHAR)MyAllocatePoolTag__(NonPagedPool, i = Str1->Length + Str2->Length + sizeof(WCHAR), UDF_UNC_STR_TAG);
773     if(!tmp)
774         return STATUS_INSUFFICIENT_RESOURCES;
775     RtlCopyMemory(tmp, Str1->Buffer, Str1->Length);
776     RtlCopyMemory(((PCHAR)tmp)+Str1->Length, Str2->Buffer, Str2->Length);*/
777     tmp[(i / sizeof(WCHAR)) - 1] = 0;
778     Str1->Length = i - sizeof(WCHAR);
779     //MyFreePool__(Str1->Buffer);
780 #ifdef UDF_DBG
781     if(Str1->Buffer && (Str1->Length >= 2*sizeof(WCHAR))) {
782         ASSERT((Str1->Buffer[0] != L'\\') || (Str1->Buffer[1] != L'\\'));
783     }
784 #endif // UDF_DBG
785     return STATUS_SUCCESS;
786 
787 #undef UDF_UNC_STR_TAG
788 
789 } // end MyAppendUnicodeStringToString()
790 
791 NTSTATUS
792 MyAppendUnicodeToString_(
793     IN PUNICODE_STRING Str1,
794     IN PCWSTR Str2
795 #ifdef UDF_TRACK_UNICODE_STR
796    ,IN PCHAR Tag
797 #endif
798     )
799 {
800     PWCHAR tmp;
801     USHORT i;
802 
803 #ifdef UDF_TRACK_UNICODE_STR
804   #define UDF_UNC_STR_TAG Tag
805 #else
806   #define UDF_UNC_STR_TAG "AppStr"
807 #endif
808 
809 #if defined(_X86_) && defined(_MSC_VER) && !defined(__clang__)
810 
811     __asm push  ebx
812     __asm push  esi
813 
814     __asm xor   ebx,ebx
815     __asm mov   esi,Str2
816 Scan_1:
817     __asm cmp   [word ptr esi+ebx],0
818     __asm je    EO_Scan
819     __asm add   ebx,2
820     __asm jmp   Scan_1
821 EO_Scan:
822     __asm mov   i,bx
823 
824     __asm pop   esi
825     __asm pop   ebx
826 
827 #else   // NO X86 optimization, use generic C/C++
828 
829     i=0;
830     while(Str2[i]) {
831        i++;
832     }
833     i *= sizeof(WCHAR);
834 
835 #endif // _X86_
836 
837     tmp = Str1->Buffer;
838     ASSERT(Str1->MaximumLength);
839     if((Str1->Length+i+sizeof(WCHAR)) > Str1->MaximumLength) {
840         if(!MyReallocPool__((PCHAR)tmp, Str1->MaximumLength,
841                          (PCHAR*)&tmp, STRING_BUFFER_ALIGN(i + Str1->Length + sizeof(WCHAR))*2 ) ) {
842             return STATUS_INSUFFICIENT_RESOURCES;
843         }
844         Str1->MaximumLength = STRING_BUFFER_ALIGN(i + sizeof(WCHAR))*2;
845         Str1->Buffer = tmp;
846     }
847     RtlCopyMemory(((PCHAR)tmp)+Str1->Length, Str2, i);
848     i+=Str1->Length;
849     tmp[(i / sizeof(WCHAR))] = 0;
850     Str1->Length = i;
851 #ifdef UDF_DBG
852 /*    if(Str1->Buffer && (Str1->Length >= 2*sizeof(WCHAR))) {
853         ASSERT((Str1->Buffer[0] != L'\\') || (Str1->Buffer[1] != L'\\'));
854     }*/
855 #endif // UDF_DBG
856     return STATUS_SUCCESS;
857 
858 #undef UDF_UNC_STR_TAG
859 
860 } // end MyAppendUnicodeToString_()
861 
862 NTSTATUS
863 MyInitUnicodeString(
864     IN PUNICODE_STRING Str1,
865     IN PCWSTR Str2
866     )
867 {
868 
869     USHORT i;
870 
871 #if defined(_X86_) && defined(_MSC_VER) && !defined(__clang__)
872 
873     __asm push  ebx
874     __asm push  esi
875 
876     __asm xor   ebx,ebx
877     __asm mov   esi,Str2
878 Scan_1:
879     __asm cmp   [word ptr esi+ebx],0
880     __asm je    EO_Scan
881     __asm add   ebx,2
882     __asm jmp   Scan_1
883 EO_Scan:
884     __asm mov   i,bx
885 
886     __asm pop   esi
887     __asm pop   ebx
888 
889 #else   // NO X86 optimization, use generic C/C++
890 
891     i=0;
892     while(Str2[i]) {
893        i++;
894     }
895     i *= sizeof(WCHAR);
896 
897 #endif // _X86_
898 
899     Str1->MaximumLength = STRING_BUFFER_ALIGN((Str1->Length = i) + sizeof(WCHAR));
900     Str1->Buffer = (PWCHAR)MyAllocatePool__(NonPagedPool, Str1->MaximumLength);
901     if(!Str1->Buffer)
902         return STATUS_INSUFFICIENT_RESOURCES;
903     RtlCopyMemory(Str1->Buffer, Str2, i);
904     Str1->Buffer[i/sizeof(WCHAR)] = 0;
905     return STATUS_SUCCESS;
906 
907 } // end MyInitUnicodeString()
908 
909 NTSTATUS
910 MyCloneUnicodeString(
911     IN PUNICODE_STRING Str1,
912     IN PUNICODE_STRING Str2
913     )
914 {
915     Str1->MaximumLength = STRING_BUFFER_ALIGN((Str1->Length = Str2->Length) + sizeof(WCHAR));
916     Str1->Buffer = (PWCHAR)MyAllocatePool__(NonPagedPool, Str1->MaximumLength);
917     if(!Str1->Buffer)
918         return STATUS_INSUFFICIENT_RESOURCES;
919     ASSERT(Str2->Buffer);
920     RtlCopyMemory(Str1->Buffer, Str2->Buffer, Str2->Length);
921     Str1->Buffer[Str1->Length/sizeof(WCHAR)] = 0;
922     return STATUS_SUCCESS;
923 
924 } // end MyCloneUnicodeString()
925 
926 /*
927     This routine checks do we needn't read something from disk to
928     obtain Attributes & so on
929  */
930 BOOLEAN
931 UDFIsDirInfoCached(
932     IN PVCB Vcb,
933     IN PUDF_FILE_INFO DirInfo
934     )
935 {
936     PDIR_INDEX_HDR hDirNdx = DirInfo->Dloc->DirIndex;
937     PDIR_INDEX_ITEM DirNdx;
938     for(uint_di i=2; (DirNdx = UDFDirIndex(hDirNdx,i)); i++) {
939         if(!(DirNdx->FI_Flags & UDF_FI_FLAG_SYS_ATTR) ||
940             (DirNdx->FI_Flags & UDF_FI_FLAG_LINKED)) return FALSE;
941     }
942     return TRUE;
943 } // end UDFIsDirInfoCached()
944 
945 #ifndef UDF_READ_ONLY_BUILD
946 NTSTATUS
947 UDFDoesOSAllowFileToBeTargetForRename__(
948     IN PUDF_FILE_INFO FileInfo
949     )
950 {
951 #ifndef _CONSOLE
952     NTSTATUS RC;
953 #endif //_CONSOLE
954 
955     if(UDFIsADirectory(FileInfo))
956         return STATUS_ACCESS_DENIED;
957     if(!FileInfo->ParentFile)
958         return STATUS_ACCESS_DENIED;
959 
960     if(UDFAttributesToNT(UDFDirIndex(UDFGetDirIndexByFileInfo(FileInfo),FileInfo->Index),
961                          FileInfo->Dloc->FileEntry) & FILE_ATTRIBUTE_READONLY)
962         return STATUS_ACCESS_DENIED;
963 
964     if(!FileInfo->Fcb)
965         return STATUS_SUCCESS;
966 #ifndef _CONSOLE
967     RC = UDFCheckAccessRights(NULL, NULL, FileInfo->Fcb, NULL, DELETE, 0);
968     if(!NT_SUCCESS(RC))
969         return RC;
970 #endif //_CONSOLE
971     if(!FileInfo->Fcb)
972         return STATUS_SUCCESS;
973 //    RC = UDFMarkStreamsForDeletion(FileInfo->Fcb->Vcb, FileInfo->Fcb, TRUE); // Delete
974 /*    RC = UDFSetDispositionInformation(FileInfo->Fcb, NULL,
975                 FileInfo->Fcb->Vcb, NULL, TRUE);
976     if(NT_SUCCESS(RC)) {
977         FileInfo->Fcb->FCBFlags |= UDF_FCB_DELETED;
978         if(UDFGetFileLinkCount(FileInfo) <= 1) {
979             FileInfo->Fcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_DELETED;
980         }
981     }
982     return RC;*/
983     return STATUS_ACCESS_DENIED;
984 
985 } // end UDFDoesOSAllowFileToBeTargetForRename__()
986 
987 NTSTATUS
988 UDFDoesOSAllowFileToBeUnlinked__(
989     IN PUDF_FILE_INFO FileInfo
990     )
991 {
992     PDIR_INDEX_HDR hCurDirNdx;
993     PDIR_INDEX_ITEM CurDirNdx;
994     uint_di i;
995 //    IO_STATUS_BLOCK IoStatus;
996 
997     ASSERT(FileInfo->Dloc);
998 
999     if(!FileInfo->ParentFile)
1000         return STATUS_CANNOT_DELETE;
1001     if(FileInfo->Dloc->SDirInfo)
1002         return STATUS_CANNOT_DELETE;
1003     if(!UDFIsADirectory(FileInfo))
1004         return STATUS_SUCCESS;
1005 
1006 //    UDFFlushAFile(FileInfo->Fcb, NULL, &IoStatus, 0);
1007     hCurDirNdx = FileInfo->Dloc->DirIndex;
1008     // check if we can delete all files
1009     for(i=2; (CurDirNdx = UDFDirIndex(hCurDirNdx,i)); i++) {
1010         // try to open Stream
1011         if(CurDirNdx->FileInfo)
1012             return STATUS_CANNOT_DELETE;
1013     }
1014 //    return UDFCheckAccessRights(NULL, NULL, FileInfo->Fcb, NULL, DELETE, 0);
1015     return STATUS_SUCCESS;
1016 } // end UDFDoesOSAllowFileToBeUnlinked__()
1017 
1018 NTSTATUS
1019 UDFDoesOSAllowFilePretendDeleted__(
1020     IN PUDF_FILE_INFO FileInfo
1021     )
1022 {
1023     PDIR_INDEX_HDR hDirNdx = UDFGetDirIndexByFileInfo(FileInfo);
1024     if(!hDirNdx) return STATUS_CANNOT_DELETE;
1025     PDIR_INDEX_ITEM DirNdx = UDFDirIndex(hDirNdx, FileInfo->Index);
1026     if(!DirNdx) return STATUS_CANNOT_DELETE;
1027     // we can't hide file that is not marked as deleted
1028     if(!(DirNdx->FileCharacteristics & FILE_DELETED)) {
1029         BrutePoint();
1030 
1031 #ifndef _CONSOLE
1032         if(!(FileInfo->Fcb->FCBFlags & (UDF_FCB_DELETE_ON_CLOSE |
1033                                         UDF_FCB_DELETED) ))
1034 #endif //_CONSOLE
1035 
1036             return STATUS_CANNOT_DELETE;
1037     }
1038     return STATUS_SUCCESS;
1039 }
1040 #endif //UDF_READ_ONLY_BUILD
1041 
1042