[CDFS]
[reactos.git] / reactos / drivers / filesystems / cdfs / misc.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002, 2004 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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * FILE: drivers/filesystems/cdfs/misc.c
23 * PURPOSE: CDROM (ISO 9660) filesystem driver
24 * PROGRAMMER: Eric Kohl
25 * UPDATE HISTORY:
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include "cdfs.h"
31
32 #define NDEBUG
33 #include <debug.h>
34
35 /* FUNCTIONS ****************************************************************/
36
37 /*
38 * FUNCTION: Used with IRP to set them to TopLevelIrp field
39 * ARGUMENTS:
40 * Irp = The IRP to set
41 * RETURNS: TRUE if top level was null, else FALSE
42 */
43 BOOLEAN
44 CdfsIsIrpTopLevel(
45 PIRP Irp)
46 {
47 BOOLEAN ReturnCode = FALSE;
48
49 DPRINT("CdfsIsIrpTopLevel()\n");
50
51 if (IoGetTopLevelIrp() == NULL)
52 {
53 IoSetTopLevelIrp(Irp);
54 ReturnCode = TRUE;
55 }
56
57 return ReturnCode;
58 }
59
60
61 /*
62 * FUNCTION: Allocate and fill a CDFS_IRP_CONTEXT struct in order to use it for IRP
63 * ARGUMENTS:
64 * DeviceObject = Used to fill in struct
65 * Irp = The IRP that need IRP_CONTEXT struct
66 * RETURNS: NULL or PCDFS_IRP_CONTEXT
67 */
68 PCDFS_IRP_CONTEXT
69 CdfsAllocateIrpContext(
70 PDEVICE_OBJECT DeviceObject,
71 PIRP Irp)
72 {
73 PCDFS_IRP_CONTEXT IrpContext;
74
75 DPRINT("CdfsAllocateIrpContext()\n");
76
77 IrpContext = (PCDFS_IRP_CONTEXT)ExAllocateFromNPagedLookasideList(&CdfsGlobalData->IrpContextLookasideList);
78 if (IrpContext == NULL)
79 return NULL;
80
81 RtlZeroMemory(IrpContext, sizeof(CDFS_IRP_CONTEXT));
82
83 // IrpContext->Identifier.Type = NTFS_TYPE_IRP_CONTEST;
84 // IrpContext->Identifier.Size = sizeof(NTFS_IRP_CONTEXT);
85 IrpContext->Irp = Irp;
86 IrpContext->DeviceObject = DeviceObject;
87 IrpContext->Stack = IoGetCurrentIrpStackLocation(Irp);
88 IrpContext->MajorFunction = IrpContext->Stack->MajorFunction;
89 IrpContext->MinorFunction = IrpContext->Stack->MinorFunction;
90 IrpContext->FileObject = IrpContext->Stack->FileObject;
91 IrpContext->IsTopLevel = (IoGetTopLevelIrp() == Irp);
92 IrpContext->PriorityBoost = IO_NO_INCREMENT;
93 IrpContext->Flags = IRPCONTEXT_COMPLETE;
94
95 if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL ||
96 IrpContext->MajorFunction == IRP_MJ_DEVICE_CONTROL ||
97 IrpContext->MajorFunction == IRP_MJ_SHUTDOWN ||
98 (IrpContext->MajorFunction != IRP_MJ_CLEANUP &&
99 IrpContext->MajorFunction != IRP_MJ_CLOSE &&
100 IoIsOperationSynchronous(Irp)))
101 {
102 IrpContext->Flags |= IRPCONTEXT_CANWAIT;
103 }
104
105 return IrpContext;
106 }
107
108
109 VOID
110 CdfsSwapString(PWCHAR Out,
111 PUCHAR In,
112 ULONG Count)
113 {
114 PUCHAR t = (PUCHAR)Out;
115 ULONG i;
116
117 for (i = 0; i < Count; i += 2)
118 {
119 t[i] = In[i+1];
120 t[i+1] = In[i];
121 if (t[i+1] == 0 && t[i] == ';')
122 break;
123 }
124 if ((i>2)&&(t[i-2] == '.'))
125 {
126 t[i-2] = 0;
127 t[i-1] = 0;
128 }
129 t[i] = 0;
130 t[i+1] = 0;
131 }
132
133
134 VOID
135 CdfsDateTimeToSystemTime(PFCB Fcb,
136 PLARGE_INTEGER SystemTime)
137 {
138 TIME_FIELDS TimeFields;
139 LARGE_INTEGER LocalTime;
140
141 TimeFields.Milliseconds = 0;
142 TimeFields.Second = Fcb->Entry.Second;
143 TimeFields.Minute = Fcb->Entry.Minute;
144 TimeFields.Hour = Fcb->Entry.Hour;
145
146 TimeFields.Day = Fcb->Entry.Day;
147 TimeFields.Month = Fcb->Entry.Month;
148 TimeFields.Year = Fcb->Entry.Year + 1900;
149
150 RtlTimeFieldsToTime(&TimeFields,
151 &LocalTime);
152 ExLocalTimeToSystemTime(&LocalTime, SystemTime);
153 }
154
155
156 VOID
157 CdfsFileFlagsToAttributes(PFCB Fcb,
158 PULONG FileAttributes)
159 {
160 /* FIXME: Fix attributes */
161
162 *FileAttributes = // FILE_ATTRIBUTE_READONLY |
163 ((Fcb->Entry.FileFlags & FILE_FLAG_HIDDEN) ? FILE_ATTRIBUTE_HIDDEN : 0) |
164 ((Fcb->Entry.FileFlags & FILE_FLAG_DIRECTORY) ? FILE_ATTRIBUTE_DIRECTORY : 0) |
165 ((Fcb->Entry.FileFlags & FILE_FLAG_SYSTEM) ? FILE_ATTRIBUTE_SYSTEM : 0) |
166 ((Fcb->Entry.FileFlags & FILE_FLAG_READONLY) ? FILE_ATTRIBUTE_READONLY : 0);
167 }
168
169 BOOLEAN
170 CdfsIsNameLegalDOS8Dot3(IN UNICODE_STRING FileName
171 )
172 {
173 ULONG i;
174 STRING DbcsName;
175 CHAR DbcsNameBuffer[12];
176
177 /* 8dot3 filename is max 12 length */
178 if (FileName.Length / sizeof(WCHAR) > 12)
179 {
180 return FALSE;
181 }
182
183 ASSERT(FileName.Length >= sizeof(WCHAR));
184 for (i = 0; i < FileName.Length / sizeof(WCHAR) ; i++)
185 {
186 /* Don't allow spaces in FileName */
187 if (FileName.Buffer[i] == L' ')
188 return FALSE;
189 }
190
191 /* If FileName is finishing with a dot, remove it */
192 if (FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] == '.')
193 {
194 FileName.Length -= sizeof(WCHAR);
195 }
196
197 /* Finally, convert the string to call the FsRtl function */
198 RtlInitEmptyAnsiString(&DbcsName, DbcsNameBuffer, sizeof(DbcsNameBuffer));
199 if (!NT_SUCCESS(RtlUnicodeStringToCountedOemString(&DbcsName,
200 &FileName,
201 FALSE)))
202 {
203
204 return FALSE;
205 }
206 return FsRtlIsFatDbcsLegal(DbcsName, FALSE, FALSE, FALSE);
207 }
208
209 BOOLEAN
210 CdfsIsRecordValid(IN PDEVICE_EXTENSION DeviceExt,
211 IN PDIR_RECORD Record)
212 {
213 if (Record->RecordLength < Record->FileIdLength + FIELD_OFFSET(DIR_RECORD, FileId))
214 {
215 DPRINT1("Found corrupted entry! %u - %u\n", Record->RecordLength, Record->FileIdLength + FIELD_OFFSET(DIR_RECORD, FileId));
216 return FALSE;
217 }
218
219 if (Record->FileIdLength == 0)
220 {
221 DPRINT1("Found corrupted entry (null size)!\n");
222 return FALSE;
223 }
224
225 if (DeviceExt->CdInfo.JolietLevel == 0)
226 {
227 if (Record->FileId[0] == ANSI_NULL && Record->FileIdLength != 1)
228 {
229 DPRINT1("Found corrupted entry!\n");
230 return FALSE;
231 }
232 }
233 else
234 {
235 if (Record->FileIdLength & 1 && Record->FileIdLength != 1)
236 {
237 DPRINT1("Found corrupted entry! %u\n", Record->FileIdLength);
238 return FALSE;
239 }
240
241 if (Record->FileIdLength == 1 && Record->FileId[0] != 0 && Record->FileId[0] != 1)
242 {
243 DPRINT1("Found corrupted entry! %c\n", Record->FileId[0]);
244 DPRINT1("%wc\n", ((PWSTR)Record->FileId)[0]);
245 return FALSE;
246 }
247
248 if (((PWSTR)Record->FileId)[0] == UNICODE_NULL)
249 {
250 DPRINT1("Found corrupted entry!\n");
251 return FALSE;
252 }
253 }
254
255 return TRUE;
256 }
257
258 VOID
259 CdfsShortNameCacheGet
260 (PFCB DirectoryFcb,
261 PLARGE_INTEGER StreamOffset,
262 PUNICODE_STRING LongName,
263 PUNICODE_STRING ShortName)
264 {
265 PLIST_ENTRY Entry;
266 PCDFS_SHORT_NAME ShortNameEntry;
267 GENERATE_NAME_CONTEXT Context = { 0 };
268
269 DPRINT("CdfsShortNameCacheGet(%I64d,%wZ)\n", StreamOffset->QuadPart, LongName);
270
271 /* Get the name list resource */
272 ExAcquireResourceExclusiveLite(&DirectoryFcb->NameListResource, TRUE);
273
274 /* Try to find the name in our cache */
275 for (Entry = DirectoryFcb->ShortNameList.Flink;
276 Entry != &DirectoryFcb->ShortNameList;
277 Entry = Entry->Flink)
278 {
279 ShortNameEntry = CONTAINING_RECORD(Entry, CDFS_SHORT_NAME, Entry);
280 if (ShortNameEntry->StreamOffset.QuadPart == StreamOffset->QuadPart)
281 {
282 /* Cache hit */
283 RtlCopyUnicodeString(ShortName, &ShortNameEntry->Name);
284 ExReleaseResourceLite(&DirectoryFcb->NameListResource);
285 DPRINT("Yield short name %wZ from cache\n", ShortName);
286 return;
287 }
288 }
289
290 /* Cache miss */
291 if (!CdfsIsNameLegalDOS8Dot3(*LongName))
292 {
293 RtlGenerate8dot3Name(LongName, FALSE, &Context, ShortName);
294 }
295 else
296 {
297 /* copy short name */
298 RtlUpcaseUnicodeString
299 (ShortName,
300 LongName,
301 FALSE);
302 }
303
304 DPRINT("Initial Guess %wZ\n", ShortName);
305
306 /* Make it unique by scanning the cache and bumping */
307 /* Note that incrementing the ambiguous name is enough, since we add new
308 * entries at the tail. We'll scan over all collisions. */
309 /* XXX could perform better. */
310 for (Entry = DirectoryFcb->ShortNameList.Flink;
311 Entry != &DirectoryFcb->ShortNameList;
312 Entry = Entry->Flink)
313 {
314 ShortNameEntry = CONTAINING_RECORD(Entry, CDFS_SHORT_NAME, Entry);
315 if (RtlCompareUnicodeString
316 (ShortName,
317 &ShortNameEntry->Name,
318 TRUE) == 0) /* Match */
319 {
320 RtlGenerate8dot3Name(LongName, FALSE, &Context, ShortName);
321 DPRINT("Collide; try %wZ\n", ShortName);
322 }
323 }
324
325 /* We've scanned over all entries and now have a unique one. Cache it. */
326 ShortNameEntry = ExAllocatePoolWithTag(PagedPool,
327 sizeof(CDFS_SHORT_NAME),
328 CDFS_SHORT_NAME_TAG);
329 if (!ShortNameEntry)
330 {
331 /* We couldn't cache it, but we can return it. We run the risk of
332 * generating a non-unique name later. */
333 ExReleaseResourceLite(&DirectoryFcb->NameListResource);
334 DPRINT1("Couldn't cache potentially clashing 8.3 name %wZ\n", ShortName);
335 return;
336 }
337
338 ShortNameEntry->StreamOffset = *StreamOffset;
339 RtlInitEmptyUnicodeString(&ShortNameEntry->Name,
340 ShortNameEntry->NameBuffer,
341 sizeof(ShortNameEntry->NameBuffer));
342 RtlCopyUnicodeString(&ShortNameEntry->Name, ShortName);
343 InsertTailList(&DirectoryFcb->ShortNameList, &ShortNameEntry->Entry);
344 ExReleaseResourceLite(&DirectoryFcb->NameListResource);
345
346 DPRINT("Returning short name %wZ for long name %wZ\n", ShortName, LongName);
347 }
348
349 /* EOF */