- Merge from trunk up to r45543
[reactos.git] / 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
323 /* Try to detect the file system */
324 #ifndef _M_ARM
325 FileData[DeviceId].FileFuncTable = IsoMount(DeviceId);
326 if (!FileData[DeviceId].FileFuncTable)
327 #endif
328 FileData[DeviceId].FileFuncTable = FatMount(DeviceId);
329 #ifndef _M_ARM
330 if (!FileData[DeviceId].FileFuncTable)
331 FileData[DeviceId].FileFuncTable = NtfsMount(DeviceId);
332 if (!FileData[DeviceId].FileFuncTable)
333 FileData[DeviceId].FileFuncTable = Ext2Mount(DeviceId);
334 #endif
335 if (!FileData[DeviceId].FileFuncTable)
336 {
337 /* Error, unable to detect file system */
338 pDevice->FuncTable->Close(DeviceId);
339 FileData[DeviceId].FuncTable = NULL;
340 return ENODEV;
341 }
342
343 pDevice->DeviceId = DeviceId;
344 }
345 else
346 {
347 DeviceId = pDevice->DeviceId;
348 }
349 pDevice->ReferenceCount++;
350 break;
351 }
352 pEntry = pEntry->Flink;
353 }
354 if (pEntry == &DeviceListHead)
355 return ENODEV;
356
357 /* At this point, device is found and opened. Its file id is stored
358 * in DeviceId, and FileData[DeviceId].FileFuncTable contains what
359 * needs to be called to open the file */
360
361 /* Search some room for the device */
362 for (i = 0; i < MAX_FDS; i++)
363 if (!FileData[i].FuncTable)
364 break;
365 if (i == MAX_FDS)
366 return EMFILE;
367
368 /* Skip leading backslash, if any */
369 if (*FileName == '\\')
370 FileName++;
371
372 /* Open the file */
373 FileData[i].FuncTable = FileData[DeviceId].FileFuncTable;
374 FileData[i].DeviceId = DeviceId;
375 *FileId = i;
376 ret = FileData[i].FuncTable->Open(FileName, OpenMode, FileId);
377 if (ret != ESUCCESS)
378 {
379 FileData[i].FuncTable = NULL;
380 *FileId = MAX_FDS;
381 }
382 return ret;
383 }
384
385 LONG ArcRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
386 {
387 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
388 return EBADF;
389 return FileData[FileId].FuncTable->Read(FileId, Buffer, N, Count);
390 }
391
392 LONG ArcSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
393 {
394 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
395 return EBADF;
396 return FileData[FileId].FuncTable->Seek(FileId, Position, SeekMode);
397 }
398
399 VOID FsRegisterDevice(CHAR* Prefix, const DEVVTBL* FuncTable)
400 {
401 DEVICE* pNewEntry;
402 ULONG dwLength;
403
404 DPRINTM(DPRINT_FILESYSTEM, "FsRegisterDevice() Prefix = %s\n", Prefix);
405
406 dwLength = strlen(Prefix) + 1;
407 pNewEntry = MmHeapAlloc(sizeof(DEVICE) + dwLength);
408 if (!pNewEntry)
409 return;
410 pNewEntry->FuncTable = FuncTable;
411 pNewEntry->ReferenceCount = 0;
412 pNewEntry->Prefix = (CHAR*)(pNewEntry + 1);
413 memcpy(pNewEntry->Prefix, Prefix, dwLength);
414
415 InsertHeadList(&DeviceListHead, &pNewEntry->ListEntry);
416 }
417
418 LPCWSTR FsGetServiceName(ULONG FileId)
419 {
420 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
421 return NULL;
422 return FileData[FileId].FuncTable->ServiceName;
423 }
424
425 VOID FsSetDeviceSpecific(ULONG FileId, VOID* Specific)
426 {
427 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
428 return;
429 FileData[FileId].Specific = Specific;
430 }
431
432 VOID* FsGetDeviceSpecific(ULONG FileId)
433 {
434 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
435 return NULL;
436 return FileData[FileId].Specific;
437 }
438
439 ULONG FsGetDeviceId(ULONG FileId)
440 {
441 if (FileId >= MAX_FDS)
442 return (ULONG)-1;
443 return FileData[FileId].DeviceId;
444 }
445
446 VOID FsInit(VOID)
447 {
448 ULONG i;
449
450 RtlZeroMemory(FileData, sizeof(FileData));
451 for (i = 0; i < MAX_FDS; i++)
452 FileData[i].DeviceId = (ULONG)-1;
453
454 InitializeListHead(&DeviceListHead);
455 }