fe8d8dbe179fcf674f08983ef25aee49a889f9ba
[reactos.git] / reactos / boot / environ / lib / io / etfs.c
1 /*
2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Library
4 * FILE: boot/environ/lib/io/etfs.c
5 * PURPOSE: Boot Library El Torito File System Management Routines
6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "bl.h"
12 #include "../drivers/filesystems/cdfs_new/cd.h"
13 typedef struct _RAW_ET_VD
14 {
15 UCHAR BootIndicator;
16 UCHAR StandardId[5];
17 UCHAR Version;
18 UCHAR SystemId[32];
19 UCHAR Reserved[32];
20 ULONG BootCatalogOffset;
21 UCHAR Padding[1973];
22 } RAW_ET_VD, *PRAW_ET_VD;
23
24 /* DATA VARIABLES ************************************************************/
25
26 typedef struct _BL_ETFS_CONTEXT
27 {
28 ULONG RootDirOffset;
29 ULONG RootDirSize;
30 ULONG BlockSize;
31 ULONG VolumeSize;
32 BOOLEAN IsIso;
33 PRAW_ISO_VD MemoryBlock;
34 ULONG Offset;
35 } BL_ETFS_CONTEXT, *PBL_ETFS_CONTEXT;
36
37 typedef struct _BL_ETFS_FILE
38 {
39 ULONG Flags;
40 ULONG DeviceId;
41 ULONG Offset;
42 ULONG Unknown;
43 ULONGLONG Size;
44 PWCHAR FsName;
45 } BL_ETFS_FILE, *PBL_ETFS_FILE;
46
47 ULONG EtfsDeviceTableEntries;
48 PVOID* EtfsDeviceTable;
49
50 NTSTATUS
51 EtfsOpen (
52 _In_ PBL_FILE_ENTRY Directory,
53 _In_ PWCHAR FileName,
54 _In_ ULONG Flags,
55 _Out_ PBL_FILE_ENTRY *FileEntry
56 );
57
58 BL_FILE_CALLBACKS EtfsFunctionTable =
59 {
60 EtfsOpen,
61 };
62
63 /* FUNCTIONS *****************************************************************/
64
65 NTSTATUS
66 EtfsOpen (
67 _In_ PBL_FILE_ENTRY Directory,
68 _In_ PWCHAR FileName,
69 _In_ ULONG Flags,
70 _Out_ PBL_FILE_ENTRY *FileEntry
71 )
72 {
73 EfiPrintf(L"Attempting to open file %s in directory %s. Not yet supported\r\n", FileName, Directory->FilePath);
74 return STATUS_NOT_IMPLEMENTED;
75 }
76
77 NTSTATUS
78 EtfspCheckCdfs (
79 _In_ PBL_ETFS_CONTEXT EtfsContext,
80 _In_ ULONG DeviceId,
81 _Out_ PRAW_ISO_VD *VolumeDescriptor,
82 _Out_ PBOOLEAN VolumeIsIso
83 )
84 {
85 EfiPrintf(L"Raw Cdfs not implemented\r\n");
86 return STATUS_NOT_IMPLEMENTED;
87 }
88
89 NTSTATUS
90 EtfspCheckEtfs (
91 _In_ PBL_ETFS_CONTEXT EtfsContext,
92 _In_ ULONG DeviceId,
93 _Out_ PRAW_ISO_VD *VolumeDescriptor,
94 _Out_ PBOOLEAN VolumeIsIso
95 )
96 {
97 PRAW_ISO_VD IsoVd;
98 PRAW_ET_VD EtVd;
99 NTSTATUS Status;
100 BOOLEAN IsIso;
101 BL_DEVICE_INFORMATION DeviceInformation;
102 ULONG Unknown, BytesRead;
103 ANSI_STRING CompareString, String;
104
105 /* Save our static buffer pointer */
106 IsoVd = EtfsContext->MemoryBlock;
107 EtVd = (PRAW_ET_VD)IsoVd;
108
109 /* First, read the El Torito Volume Descriptor */
110 BlDeviceGetInformation(DeviceId, &DeviceInformation);
111 Unknown = DeviceInformation.BlockDeviceInfo.Unknown;
112 DeviceInformation.BlockDeviceInfo.Unknown |= 1;
113 BlDeviceSetInformation(DeviceId, &DeviceInformation);
114 Status = BlDeviceReadAtOffset(DeviceId,
115 CD_SECTOR_SIZE,
116 (FIRST_VD_SECTOR + 1) * CD_SECTOR_SIZE,
117 EtfsContext->MemoryBlock,
118 &BytesRead);
119 DeviceInformation.BlockDeviceInfo.Unknown = Unknown;
120 BlDeviceSetInformation(DeviceId, &DeviceInformation);
121 if (!NT_SUCCESS(Status))
122 {
123 EfiPrintf(L" read failed\r\n");
124 return Status;
125 }
126
127 /* Remember that's where we last read */
128 EtfsContext->Offset = (FIRST_VD_SECTOR + 1) * CD_SECTOR_SIZE;
129
130 /* Check if it's EL TORITO! */
131 RtlInitString(&String, "EL TORITO SPECIFICATION");
132 CompareString.Buffer = (PCHAR)EtVd->SystemId;
133 CompareString.Length = 23;
134 CompareString.MaximumLength = 23;
135 if (!RtlEqualString(&CompareString, &String, TRUE))
136 {
137 return STATUS_UNSUCCESSFUL;
138 }
139
140 /* Check the version and boot indicator */
141 if ((EtVd->Version != 1) || (EtVd->BootIndicator))
142 {
143 return STATUS_UNSUCCESSFUL;
144 }
145
146 /* Check if it has the CD0001 identifier */
147 RtlInitString(&String, ISO_VOL_ID);
148 CompareString.Buffer = (PCHAR)EtVd->StandardId;
149 CompareString.Length = 5;
150 CompareString.MaximumLength = 5;
151 if (!RtlEqualString(&CompareString, &String, TRUE))
152 {
153 return STATUS_UNSUCCESSFUL;
154 }
155
156 /* Step two, we now want to read the ISO Volume Descriptor */
157 DeviceInformation.BlockDeviceInfo.Unknown |= 1u;
158 BlDeviceSetInformation(DeviceId, &DeviceInformation);
159 Status = BlDeviceReadAtOffset(DeviceId,
160 CD_SECTOR_SIZE,
161 FIRST_VD_SECTOR * CD_SECTOR_SIZE,
162 EtfsContext->MemoryBlock,
163 &BytesRead);
164 DeviceInformation.BlockDeviceInfo.Unknown = Unknown;
165 BlDeviceSetInformation(DeviceId, &DeviceInformation);
166 if (!NT_SUCCESS(Status))
167 {
168 return Status;
169 }
170
171 /* Remember where we left off */
172 EtfsContext->Offset = FIRST_VD_SECTOR * CD_SECTOR_SIZE;
173
174 /* This should also say CD0001 */
175 CompareString.Buffer = (PCHAR)IsoVd->StandardId;
176 CompareString.Length = 5;
177 CompareString.MaximumLength = 5;
178 IsIso = RtlEqualString(&CompareString, &String, TRUE);
179 if (!IsIso)
180 {
181 return STATUS_UNSUCCESSFUL;
182 }
183
184 /* And should be a version we support */
185 if ((IsoVd->Version != VERSION_1) || (IsoVd->DescType != VD_PRIMARY))
186 {
187 return STATUS_UNSUCCESSFUL;
188 }
189
190 /* Return back to the caller */
191 *VolumeDescriptor = IsoVd;
192 *VolumeIsIso = IsIso;
193 EfiPrintf(L"Recognized!!!\r\n");
194 return STATUS_SUCCESS;
195 }
196
197 VOID
198 EtfspGetDirectoryInfo (
199 _In_ PBL_ETFS_CONTEXT EtfsContext,
200 _In_ PRAW_DIR_REC DirEntry,
201 _Out_ PULONG FileOffset,
202 _Out_ PULONG FileSize,
203 _Out_opt_ PBOOLEAN IsDirectory
204 )
205 {
206 ULONG SectorOffset;
207 BOOLEAN IsDir;
208
209 *FileOffset = *(PULONG)DirEntry->FileLoc * EtfsContext->BlockSize;
210 *FileOffset += (DirEntry->XarLen * EtfsContext->BlockSize);
211
212 SectorOffset = ALIGN_DOWN_BY(*FileOffset, CD_SECTOR_SIZE);
213
214 *FileSize = *(PULONG)DirEntry->DataLen;
215
216 IsDir = DE_FILE_FLAGS(EtfsContext->IsIso, DirEntry) & ISO_ATTR_DIRECTORY;
217 if (IsDir)
218 {
219 *FileSize += ALIGN_UP_BY(SectorOffset, CD_SECTOR_SIZE) - SectorOffset;
220 }
221
222 if (IsDirectory)
223 {
224 *IsDirectory = IsDir;
225 }
226 }
227
228 NTSTATUS
229 EtfspDeviceContextDestroy (
230 _In_ PBL_ETFS_CONTEXT EtfsContext
231 )
232 {
233 if (EtfsContext->MemoryBlock)
234 {
235 BlMmFreeHeap(EtfsContext->MemoryBlock);
236 }
237 BlMmFreeHeap(EtfsContext);
238 return 0;
239 }
240
241 NTSTATUS
242 EtfspCreateContext (
243 _In_ ULONG DeviceId,
244 _Out_ PBL_ETFS_CONTEXT *EtfsContext
245 )
246 {
247 PBL_ETFS_CONTEXT NewContext;
248 PVOID MemoryBlock;
249 NTSTATUS Status;
250 BOOLEAN IsIso;
251 PRAW_ISO_VD RawVd;
252
253 NewContext = (PBL_ETFS_CONTEXT)BlMmAllocateHeap(sizeof(*NewContext));
254 if (!NewContext)
255 {
256 return STATUS_NO_MEMORY;
257 }
258 RtlZeroMemory(NewContext, sizeof(*NewContext));
259
260 MemoryBlock = BlMmAllocateHeap(CD_SECTOR_SIZE);
261 NewContext->MemoryBlock = MemoryBlock;
262 if (!MemoryBlock)
263 {
264 Status = STATUS_NO_MEMORY;
265 goto Quickie;
266 }
267
268 Status = EtfspCheckEtfs(NewContext, DeviceId, &RawVd, &IsIso);
269 if (!NT_SUCCESS(Status))
270 {
271 EfiPrintf(L"Drive not EDFS. Checking for CDFS: %lx\r\n");
272 Status = EtfspCheckCdfs(NewContext, DeviceId, &RawVd, &IsIso);
273 }
274
275 if (!NT_SUCCESS(Status))
276 {
277 EfiPrintf(L"Drive not CDFS. Failing: %lx\r\n");
278 goto Quickie;
279 }
280
281 NewContext->IsIso = IsIso;
282 NewContext->BlockSize = RVD_LB_SIZE(RawVd, IsIso);
283 NewContext->VolumeSize = RVD_VOL_SIZE(RawVd, IsIso);
284
285 EtfspGetDirectoryInfo(NewContext,
286 (PRAW_DIR_REC)RVD_ROOT_DE(RawVd, IsIso),
287 &NewContext->RootDirOffset,
288 &NewContext->RootDirSize,
289 0);
290
291 Quickie:
292 EtfspDeviceContextDestroy(NewContext);
293 NewContext = NULL;
294
295 *EtfsContext = NewContext;
296 return Status;
297 }
298
299 NTSTATUS
300 EtfspDeviceTableDestroyEntry (
301 _In_ PBL_ETFS_CONTEXT EtfsContext,
302 _In_ ULONG Index
303 )
304 {
305 EtfspDeviceContextDestroy(EtfsContext);
306 EtfsDeviceTable[Index] = NULL;
307
308 return STATUS_SUCCESS;
309 }
310
311 NTSTATUS
312 EtfsMount (
313 _In_ ULONG DeviceId,
314 _In_ ULONG Unknown,
315 _Out_ PBL_FILE_ENTRY* FileEntry
316 )
317 {
318 PBL_ETFS_CONTEXT EtfsContext = NULL;
319 PBL_FILE_ENTRY RootEntry;
320 NTSTATUS Status;
321 PBL_ETFS_FILE EtfsFile;
322
323 EfiPrintf(L"Trying to mount as ETFS...\r\n");
324
325 Status = EtfspCreateContext(DeviceId, &EtfsContext);
326 if (!NT_SUCCESS(Status))
327 {
328 EfiPrintf(L"ETFS context failed: %lx\r\n");
329 return Status;
330 }
331
332 Status = BlTblSetEntry(&EtfsDeviceTable,
333 &EtfsDeviceTableEntries,
334 EtfsContext,
335 &DeviceId,
336 TblDoNotPurgeEntry);
337 if (!NT_SUCCESS(Status))
338 {
339 EtfspDeviceContextDestroy(EtfsContext);
340 return Status;
341 }
342
343 RootEntry = BlMmAllocateHeap(sizeof(*RootEntry));
344 if (!RootEntry)
345 {
346 Status = STATUS_NO_MEMORY;
347 goto Quickie;
348 }
349
350 RtlZeroMemory(RootEntry, sizeof(*RootEntry));
351
352 RootEntry->FilePath = BlMmAllocateHeap(4);
353 if (!RootEntry->FilePath)
354 {
355 Status = STATUS_NO_MEMORY;
356 goto Quickie;
357 }
358
359 wcsncpy(RootEntry->FilePath, L"\\", 1);
360
361 RootEntry->DeviceId = DeviceId;
362 RtlCopyMemory(&RootEntry->Callbacks,
363 &EtfsFunctionTable,
364 sizeof(RootEntry->Callbacks));
365
366 EtfsFile = (PBL_ETFS_FILE)BlMmAllocateHeap(sizeof(*EtfsFile));
367 if (!EtfsFile)
368 {
369 Status = STATUS_NO_MEMORY;
370 goto Quickie;
371 }
372
373 RootEntry->Flags |= 0x10000;
374
375 RtlZeroMemory(EtfsFile, sizeof(*EtfsFile));
376 RootEntry->FsSpecificData = EtfsFile;
377 EtfsFile->DeviceId = DeviceId;
378 EtfsFile->Flags |= 1;
379 EtfsFile->Offset = EtfsContext->RootDirOffset;
380 EtfsFile->Unknown = 0;
381 EtfsFile->Size = EtfsContext->RootDirSize;
382 EtfsFile->FsName = L"cdfs";
383 *FileEntry = RootEntry;
384
385 return STATUS_SUCCESS;
386
387 Quickie:
388 if (RootEntry->FilePath)
389 {
390 BlMmFreeHeap(RootEntry->FilePath);
391 }
392 if (RootEntry->FsSpecificData)
393 {
394 BlMmFreeHeap(RootEntry->FsSpecificData);
395 }
396 if (RootEntry)
397 {
398 BlMmFreeHeap(RootEntry);
399 }
400
401 EtfspDeviceTableDestroyEntry(EtfsContext, DeviceId);
402
403 return Status;
404 }
405
406 NTSTATUS
407 EtfsInitialize (
408 VOID
409 )
410 {
411 NTSTATUS Status;
412
413 /* Allocate the device table with 2 entries*/
414 EtfsDeviceTableEntries = 2;
415 EtfsDeviceTable = BlMmAllocateHeap(sizeof(PBL_FILE_ENTRY) *
416 EtfsDeviceTableEntries);
417 if (EtfsDeviceTable)
418 {
419 /* Zero it out */
420 RtlZeroMemory(EtfsDeviceTable,
421 sizeof(PBL_FILE_ENTRY) * EtfsDeviceTableEntries);
422 Status = STATUS_SUCCESS;
423 }
424 else
425 {
426 /* No memory, fail */
427 Status = STATUS_NO_MEMORY;
428 }
429
430 /* Return back to caller */
431 return Status;
432 }
433