[freeldr] Add PXE "filesystem"
[reactos.git] / reactos / boot / freeldr / freeldr / fs / fs.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 * Copyright (C) 2008-2009 Hervé Poussineau <hpoussin@reactos.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <freeldr.h>
22
23 #define NDEBUG
24 #include <debug.h>
25
26 /////////////////////////////////////////////////////////////////////////////////////////////
27 // FUNCTIONS
28 /////////////////////////////////////////////////////////////////////////////////////////////
29
30 VOID FileSystemError(PCSTR ErrorString)
31 {
32 DPRINTM(DPRINT_FILESYSTEM, "%s\n", ErrorString);
33
34 UiMessageBox(ErrorString);
35 }
36
37 PFILE FsOpenFile(PCSTR FileName)
38 {
39 CHAR FullPath[MAX_PATH];
40 ULONG FileId;
41 LONG ret;
42
43 //
44 // Print status message
45 //
46 DPRINTM(DPRINT_FILESYSTEM, "Opening file '%s'...\n", FileName);
47
48 //
49 // Create full file name
50 //
51 MachDiskGetBootPath(FullPath, sizeof(FullPath));
52 strcat(FullPath, FileName);
53
54 //
55 // Open the file
56 //
57 ret = ArcOpen(FullPath, OpenReadOnly, &FileId);
58
59 //
60 // Check for success
61 //
62 if (ret == ESUCCESS)
63 return (PFILE)FileId;
64 else
65 return (PFILE)0;
66 }
67
68 VOID FsCloseFile(PFILE FileHandle)
69 {
70 ULONG FileId = (ULONG)FileHandle;
71
72 //
73 // Close the handle
74 //
75 ArcClose(FileId);
76
77 //
78 // Do not check for error; this function is
79 // supposed to always succeed
80 //
81 }
82
83 /*
84 * ReadFile()
85 * returns number of bytes read or EOF
86 */
87 BOOLEAN FsReadFile(PFILE FileHandle, ULONG BytesToRead, ULONG* BytesRead, PVOID Buffer)
88 {
89 ULONG FileId = (ULONG)FileHandle;
90 LONG ret;
91
92 //
93 // Read the file
94 //
95 ret = ArcRead(FileId, Buffer, BytesToRead, BytesRead);
96
97 //
98 // Check for success
99 //
100 if (ret == ESUCCESS)
101 return TRUE;
102 else
103 return FALSE;
104 }
105
106 ULONG FsGetFileSize(PFILE FileHandle)
107 {
108 ULONG FileId = (ULONG)FileHandle;
109 FILEINFORMATION Information;
110 LONG ret;
111
112 //
113 // Query file informations
114 //
115 ret = ArcGetFileInformation(FileId, &Information);
116
117 //
118 // Check for error
119 //
120 if (ret != ESUCCESS || Information.EndingAddress.HighPart != 0)
121 return 0;
122
123 //
124 // Return file size
125 //
126 return Information.EndingAddress.LowPart;
127 }
128
129 VOID FsSetFilePointer(PFILE FileHandle, ULONG NewFilePointer)
130 {
131 ULONG FileId = (ULONG)FileHandle;
132 LARGE_INTEGER Position;
133
134 //
135 // Set file position
136 //
137 Position.HighPart = 0;
138 Position.LowPart = NewFilePointer;
139 ArcSeek(FileId, &Position, SeekAbsolute);
140
141 //
142 // Do not check for error; this function is
143 // supposed to always succeed
144 //
145 }
146
147 /*
148 * FsGetNumPathParts()
149 * This function parses a path in the form of dir1\dir2\file1.ext
150 * and returns the number of parts it has (i.e. 3 - dir1,dir2,file1.ext)
151 */
152 ULONG FsGetNumPathParts(PCSTR Path)
153 {
154 size_t i;
155 ULONG num;
156
157 for (i=0,num=0; i<strlen(Path); i++)
158 {
159 if ((Path[i] == '\\') || (Path[i] == '/'))
160 {
161 num++;
162 }
163 }
164 num++;
165
166 DPRINTM(DPRINT_FILESYSTEM, "FsGetNumPathParts() Path = %s NumPathParts = %d\n", Path, num);
167
168 return num;
169 }
170
171 /*
172 * FsGetFirstNameFromPath()
173 * This function parses a path in the form of dir1\dir2\file1.ext
174 * and puts the first name of the path (e.g. "dir1") in buffer
175 * compatible with the MSDOS directory structure
176 */
177 VOID FsGetFirstNameFromPath(PCHAR Buffer, PCSTR Path)
178 {
179 size_t i;
180
181 // Copy all the characters up to the end of the
182 // string or until we hit a '\' character
183 // and put them in Buffer
184 for (i=0; i<strlen(Path); i++)
185 {
186 if ((Path[i] == '\\') || (Path[i] == '/'))
187 {
188 break;
189 }
190 else
191 {
192 Buffer[i] = Path[i];
193 }
194 }
195
196 Buffer[i] = 0;
197
198 DPRINTM(DPRINT_FILESYSTEM, "FsGetFirstNameFromPath() Path = %s FirstName = %s\n", Path, Buffer);
199 }
200
201 typedef struct tagFILEDATA
202 {
203 ULONG DeviceId;
204 ULONG ReferenceCount;
205 const DEVVTBL* FuncTable;
206 const DEVVTBL* FileFuncTable;
207 VOID* Specific;
208 } FILEDATA;
209
210 typedef struct tagDEVICE
211 {
212 LIST_ENTRY ListEntry;
213 const DEVVTBL* FuncTable;
214 CHAR* Prefix;
215 ULONG DeviceId;
216 ULONG ReferenceCount;
217 } DEVICE;
218
219 static FILEDATA FileData[MAX_FDS];
220 static LIST_ENTRY DeviceListHead;
221
222 LONG ArcClose(ULONG FileId)
223 {
224 LONG ret;
225
226 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
227 return EBADF;
228
229 ret = FileData[FileId].FuncTable->Close(FileId);
230
231 if (ret == ESUCCESS)
232 {
233 FileData[FileId].FuncTable = NULL;
234 FileData[FileId].Specific = NULL;
235 FileData[FileId].DeviceId = -1;
236 }
237 return ret;
238 }
239
240 LONG ArcGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
241 {
242 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
243 return EBADF;
244 return FileData[FileId].FuncTable->GetFileInformation(FileId, Information);
245 }
246
247 LONG ArcOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
248 {
249 ULONG i, ret;
250 PLIST_ENTRY pEntry;
251 DEVICE* pDevice;
252 CHAR* DeviceName;
253 CHAR* FileName;
254 CHAR* p;
255 CHAR* q;
256 ULONG dwCount, dwLength;
257 OPENMODE DeviceOpenMode;
258 ULONG DeviceId;
259
260 /* Print status message */
261 DPRINTM(DPRINT_FILESYSTEM, "Opening file '%s'...\n", Path);
262
263 *FileId = MAX_FDS;
264
265 /* Search last ')', which delimits device and path */
266 FileName = strrchr(Path, ')');
267 if (!FileName)
268 return EINVAL;
269 FileName++;
270
271 /* Count number of "()", which needs to be replaced by "(0)" */
272 dwCount = 0;
273 for (p = Path; p != FileName; p++)
274 if (*p == '(' && *(p + 1) == ')')
275 dwCount++;
276
277 /* Duplicate device name, and replace "()" by "(0)" (if required) */
278 dwLength = FileName - Path + dwCount;
279 if (dwCount != 0)
280 {
281 DeviceName = MmHeapAlloc(FileName - Path + dwCount);
282 if (!DeviceName)
283 return ENOMEM;
284 for (p = Path, q = DeviceName; p != FileName; p++)
285 {
286 *q++ = *p;
287 if (*p == '(' && *(p + 1) == ')')
288 *q++ = '0';
289 }
290 }
291 else
292 DeviceName = Path;
293
294 /* Search for the device */
295 pEntry = DeviceListHead.Flink;
296 if (OpenMode == OpenReadOnly || OpenMode == OpenWriteOnly)
297 DeviceOpenMode = OpenMode;
298 else
299 DeviceOpenMode = OpenReadWrite;
300 while (pEntry != &DeviceListHead)
301 {
302 pDevice = CONTAINING_RECORD(pEntry, DEVICE, ListEntry);
303 if (strncmp(pDevice->Prefix, DeviceName, dwLength) == 0)
304 {
305 /* OK, device found. It is already opened? */
306 if (pDevice->ReferenceCount == 0)
307 {
308 /* Search some room for the device */
309 for (DeviceId = 0; DeviceId < MAX_FDS; DeviceId++)
310 if (!FileData[DeviceId].FuncTable)
311 break;
312 if (DeviceId == MAX_FDS)
313 return EMFILE;
314 /* Try to open the device */
315 FileData[DeviceId].FuncTable = pDevice->FuncTable;
316 ret = pDevice->FuncTable->Open(pDevice->Prefix, DeviceOpenMode, &DeviceId);
317 if (ret != ESUCCESS)
318 {
319 FileData[DeviceId].FuncTable = NULL;
320 return ret;
321 }
322 else if (!*FileName)
323 {
324 /* Done, caller wanted to open the raw device */
325 *FileId = DeviceId;
326 pDevice->ReferenceCount++;
327 return ESUCCESS;
328 }
329
330 /* Try to detect the file system */
331 #ifndef _M_ARM
332 FileData[DeviceId].FileFuncTable = IsoMount(DeviceId);
333 if (!FileData[DeviceId].FileFuncTable)
334 #endif
335 FileData[DeviceId].FileFuncTable = FatMount(DeviceId);
336 #ifndef _M_ARM
337 if (!FileData[DeviceId].FileFuncTable)
338 FileData[DeviceId].FileFuncTable = NtfsMount(DeviceId);
339 if (!FileData[DeviceId].FileFuncTable)
340 FileData[DeviceId].FileFuncTable = Ext2Mount(DeviceId);
341 #endif
342 #ifdef _M_IX86
343 if (!FileData[DeviceId].FileFuncTable)
344 FileData[DeviceId].FileFuncTable = PxeMount(DeviceId);
345 #endif
346 if (!FileData[DeviceId].FileFuncTable)
347 {
348 /* Error, unable to detect file system */
349 pDevice->FuncTable->Close(DeviceId);
350 FileData[DeviceId].FuncTable = NULL;
351 return ENODEV;
352 }
353
354 pDevice->DeviceId = DeviceId;
355 }
356 else
357 {
358 DeviceId = pDevice->DeviceId;
359 }
360 pDevice->ReferenceCount++;
361 break;
362 }
363 pEntry = pEntry->Flink;
364 }
365 if (pEntry == &DeviceListHead)
366 return ENODEV;
367
368 /* At this point, device is found and opened. Its file id is stored
369 * in DeviceId, and FileData[DeviceId].FileFuncTable contains what
370 * needs to be called to open the file */
371
372 /* Search some room for the device */
373 for (i = 0; i < MAX_FDS; i++)
374 if (!FileData[i].FuncTable)
375 break;
376 if (i == MAX_FDS)
377 return EMFILE;
378
379 /* Skip leading backslash, if any */
380 if (*FileName == '\\')
381 FileName++;
382
383 /* Open the file */
384 FileData[i].FuncTable = FileData[DeviceId].FileFuncTable;
385 FileData[i].DeviceId = DeviceId;
386 *FileId = i;
387 ret = FileData[i].FuncTable->Open(FileName, OpenMode, FileId);
388 if (ret != ESUCCESS)
389 {
390 FileData[i].FuncTable = NULL;
391 *FileId = MAX_FDS;
392 }
393 return ret;
394 }
395
396 LONG ArcRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
397 {
398 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
399 return EBADF;
400 return FileData[FileId].FuncTable->Read(FileId, Buffer, N, Count);
401 }
402
403 LONG ArcSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
404 {
405 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
406 return EBADF;
407 return FileData[FileId].FuncTable->Seek(FileId, Position, SeekMode);
408 }
409
410 VOID FsRegisterDevice(CHAR* Prefix, const DEVVTBL* FuncTable)
411 {
412 DEVICE* pNewEntry;
413 ULONG dwLength;
414
415 DPRINTM(DPRINT_FILESYSTEM, "FsRegisterDevice() Prefix = %s\n", Prefix);
416
417 dwLength = strlen(Prefix) + 1;
418 pNewEntry = MmHeapAlloc(sizeof(DEVICE) + dwLength);
419 if (!pNewEntry)
420 return;
421 pNewEntry->FuncTable = FuncTable;
422 pNewEntry->ReferenceCount = 0;
423 pNewEntry->Prefix = (CHAR*)(pNewEntry + 1);
424 memcpy(pNewEntry->Prefix, Prefix, dwLength);
425
426 InsertHeadList(&DeviceListHead, &pNewEntry->ListEntry);
427 }
428
429 LPCWSTR FsGetServiceName(ULONG FileId)
430 {
431 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
432 return NULL;
433 return FileData[FileId].FuncTable->ServiceName;
434 }
435
436 VOID FsSetDeviceSpecific(ULONG FileId, VOID* Specific)
437 {
438 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
439 return;
440 FileData[FileId].Specific = Specific;
441 }
442
443 VOID* FsGetDeviceSpecific(ULONG FileId)
444 {
445 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
446 return NULL;
447 return FileData[FileId].Specific;
448 }
449
450 ULONG FsGetDeviceId(ULONG FileId)
451 {
452 if (FileId >= MAX_FDS)
453 return (ULONG)-1;
454 return FileData[FileId].DeviceId;
455 }
456
457 VOID FsInit(VOID)
458 {
459 ULONG i;
460
461 RtlZeroMemory(FileData, sizeof(FileData));
462 for (i = 0; i < MAX_FDS; i++)
463 FileData[i].DeviceId = (ULONG)-1;
464
465 InitializeListHead(&DeviceListHead);
466 }