Create the AHCI branch for Aman's work
[reactos.git] / boot / freeldr / freeldr / lib / 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 /* INCLUDES *******************************************************************/
22
23 #include <freeldr.h>
24
25 #define NDEBUG
26 #include <debug.h>
27
28 DBG_DEFAULT_CHANNEL(FILESYSTEM);
29
30 /* GLOBALS ********************************************************************/
31
32 #define TAG_DEVICE_NAME 'NDsF'
33 #define TAG_DEVICE 'vDsF'
34
35 typedef struct tagFILEDATA
36 {
37 ULONG DeviceId;
38 ULONG ReferenceCount;
39 const DEVVTBL* FuncTable;
40 const DEVVTBL* FileFuncTable;
41 VOID* Specific;
42 } FILEDATA;
43
44 typedef struct tagDEVICE
45 {
46 LIST_ENTRY ListEntry;
47 const DEVVTBL* FuncTable;
48 CHAR* Prefix;
49 ULONG DeviceId;
50 ULONG ReferenceCount;
51 } DEVICE;
52
53 static FILEDATA FileData[MAX_FDS];
54 static LIST_ENTRY DeviceListHead;
55
56 /* ARC FUNCTIONS **************************************************************/
57
58 ARC_STATUS ArcOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
59 {
60 ARC_STATUS Status;
61 ULONG Count, i;
62 PLIST_ENTRY pEntry;
63 DEVICE* pDevice;
64 CHAR* DeviceName;
65 CHAR* FileName;
66 CHAR* p;
67 CHAR* q;
68 SIZE_T Length;
69 OPENMODE DeviceOpenMode;
70 ULONG DeviceId;
71
72 /* Print status message */
73 TRACE("Opening file '%s'...\n", Path);
74
75 *FileId = MAX_FDS;
76
77 /* Search last ')', which delimits device and path */
78 FileName = strrchr(Path, ')');
79 if (!FileName)
80 return EINVAL;
81 FileName++;
82
83 /* Count number of "()", which needs to be replaced by "(0)" */
84 Count = 0;
85 for (p = Path; p != FileName; p++)
86 {
87 if (*p == '(' && *(p + 1) == ')')
88 Count++;
89 }
90
91 /* Duplicate device name, and replace "()" by "(0)" (if required) */
92 Length = FileName - Path + Count;
93 if (Count != 0)
94 {
95 DeviceName = FrLdrTempAlloc(FileName - Path + Count, TAG_DEVICE_NAME);
96 if (!DeviceName)
97 return ENOMEM;
98 for (p = Path, q = DeviceName; p != FileName; p++)
99 {
100 *q++ = *p;
101 if (*p == '(' && *(p + 1) == ')')
102 *q++ = '0';
103 }
104 }
105 else
106 {
107 DeviceName = Path;
108 }
109
110 /* Search for the device */
111 if (OpenMode == OpenReadOnly || OpenMode == OpenWriteOnly)
112 DeviceOpenMode = OpenMode;
113 else
114 DeviceOpenMode = OpenReadWrite;
115
116 pEntry = DeviceListHead.Flink;
117 while (pEntry != &DeviceListHead)
118 {
119 pDevice = CONTAINING_RECORD(pEntry, DEVICE, ListEntry);
120 if (strncmp(pDevice->Prefix, DeviceName, Length) == 0)
121 {
122 /* OK, device found. It is already opened? */
123 if (pDevice->ReferenceCount == 0)
124 {
125 /* Search some room for the device */
126 for (DeviceId = 0; DeviceId < MAX_FDS; DeviceId++)
127 {
128 if (!FileData[DeviceId].FuncTable)
129 break;
130 }
131 if (DeviceId == MAX_FDS)
132 return EMFILE;
133
134 /* Try to open the device */
135 FileData[DeviceId].FuncTable = pDevice->FuncTable;
136 Status = pDevice->FuncTable->Open(pDevice->Prefix, DeviceOpenMode, &DeviceId);
137 if (Status != ESUCCESS)
138 {
139 FileData[DeviceId].FuncTable = NULL;
140 return Status;
141 }
142 else if (!*FileName)
143 {
144 /* Done, caller wanted to open the raw device */
145 *FileId = DeviceId;
146 pDevice->ReferenceCount++;
147 return ESUCCESS;
148 }
149
150 /* Try to detect the file system */
151 #ifndef _M_ARM
152 FileData[DeviceId].FileFuncTable = IsoMount(DeviceId);
153 if (!FileData[DeviceId].FileFuncTable)
154 #endif
155 FileData[DeviceId].FileFuncTable = FatMount(DeviceId);
156 #ifndef _M_ARM
157 if (!FileData[DeviceId].FileFuncTable)
158 FileData[DeviceId].FileFuncTable = NtfsMount(DeviceId);
159 if (!FileData[DeviceId].FileFuncTable)
160 FileData[DeviceId].FileFuncTable = Ext2Mount(DeviceId);
161 #endif
162 #if defined(_M_IX86) || defined(_M_AMD64)
163 if (!FileData[DeviceId].FileFuncTable)
164 FileData[DeviceId].FileFuncTable = PxeMount(DeviceId);
165 #endif
166 if (!FileData[DeviceId].FileFuncTable)
167 {
168 /* Error, unable to detect file system */
169 pDevice->FuncTable->Close(DeviceId);
170 FileData[DeviceId].FuncTable = NULL;
171 return ENODEV;
172 }
173
174 pDevice->DeviceId = DeviceId;
175 }
176 else
177 {
178 DeviceId = pDevice->DeviceId;
179 }
180 pDevice->ReferenceCount++;
181 break;
182 }
183 pEntry = pEntry->Flink;
184 }
185 if (pEntry == &DeviceListHead)
186 return ENODEV;
187
188 /* At this point, device is found and opened. Its file id is stored
189 * in DeviceId, and FileData[DeviceId].FileFuncTable contains what
190 * needs to be called to open the file */
191
192 /* Search some room for the device */
193 for (i = 0; i < MAX_FDS; i++)
194 {
195 if (!FileData[i].FuncTable)
196 break;
197 }
198 if (i == MAX_FDS)
199 return EMFILE;
200
201 /* Skip leading backslash, if any */
202 if (*FileName == '\\')
203 FileName++;
204
205 /* Open the file */
206 FileData[i].FuncTable = FileData[DeviceId].FileFuncTable;
207 FileData[i].DeviceId = DeviceId;
208 *FileId = i;
209 Status = FileData[i].FuncTable->Open(FileName, OpenMode, FileId);
210 if (Status != ESUCCESS)
211 {
212 FileData[i].FuncTable = NULL;
213 *FileId = MAX_FDS;
214 }
215 return Status;
216 }
217
218 ARC_STATUS ArcClose(ULONG FileId)
219 {
220 ARC_STATUS Status;
221
222 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
223 return EBADF;
224
225 Status = FileData[FileId].FuncTable->Close(FileId);
226
227 if (Status == ESUCCESS)
228 {
229 FileData[FileId].FuncTable = NULL;
230 FileData[FileId].Specific = NULL;
231 FileData[FileId].DeviceId = -1;
232 }
233 return Status;
234 }
235
236 ARC_STATUS ArcRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
237 {
238 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
239 return EBADF;
240 return FileData[FileId].FuncTable->Read(FileId, Buffer, N, Count);
241 }
242
243 ARC_STATUS ArcSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
244 {
245 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
246 return EBADF;
247 return FileData[FileId].FuncTable->Seek(FileId, Position, SeekMode);
248 }
249
250 ARC_STATUS ArcGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
251 {
252 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
253 return EBADF;
254 return FileData[FileId].FuncTable->GetFileInformation(FileId, Information);
255 }
256
257 /* FUNCTIONS ******************************************************************/
258
259 VOID FileSystemError(PCSTR ErrorString)
260 {
261 ERR("%s\n", ErrorString);
262 UiMessageBox(ErrorString);
263 }
264
265 PFILE FsOpenFile(PCSTR FileName)
266 {
267 CHAR FullPath[MAX_PATH] = "";
268 ULONG FileId;
269 ARC_STATUS Status;
270
271 //
272 // Print status message
273 //
274 TRACE("Opening file '%s'...\n", FileName);
275
276 //
277 // Check whether FileName is a full path
278 // and if not, create a full file name.
279 //
280 // See ArcOpen: Search last ')', which delimits device and path.
281 //
282 if (strrchr(FileName, ')') == NULL)
283 {
284 /* This is not a full path. Use the current (i.e. boot) device. */
285 MachDiskGetBootPath(FullPath, sizeof(FullPath));
286
287 /* Append a path separator if needed */
288 if (FileName[0] != '\\' && FileName[0] != '/')
289 strcat(FullPath, "\\");
290 }
291 // Append (or just copy) the remaining file name.
292 strcat(FullPath, FileName);
293
294 //
295 // Open the file
296 //
297 Status = ArcOpen(FullPath, OpenReadOnly, &FileId);
298
299 //
300 // Check for success
301 //
302 if (Status == ESUCCESS)
303 return (PFILE)FileId;
304 else
305 return (PFILE)0;
306 }
307
308 VOID FsCloseFile(PFILE FileHandle)
309 {
310 ULONG FileId = (ULONG)FileHandle;
311
312 //
313 // Close the handle. Do not check for error,
314 // this function is supposed to always succeed.
315 //
316 ArcClose(FileId);
317 }
318
319 /*
320 * ReadFile()
321 * returns number of bytes read or EOF
322 */
323 BOOLEAN FsReadFile(PFILE FileHandle, ULONG BytesToRead, ULONG* BytesRead, PVOID Buffer)
324 {
325 ULONG FileId = (ULONG)FileHandle;
326
327 //
328 // Read the file
329 //
330 return (ArcRead(FileId, Buffer, BytesToRead, BytesRead) == ESUCCESS);
331 }
332
333 BOOLEAN FsGetFileInformation(PFILE FileHandle, FILEINFORMATION* Information)
334 {
335 ULONG FileId = (ULONG)FileHandle;
336
337 //
338 // Get file information
339 //
340 return (ArcGetFileInformation(FileId, Information) == ESUCCESS);
341 }
342
343 ULONG FsGetFileSize(PFILE FileHandle)
344 {
345 ULONG FileId = (ULONG)FileHandle;
346 FILEINFORMATION Information;
347 ARC_STATUS Status;
348
349 //
350 // Query file informations
351 //
352 Status = ArcGetFileInformation(FileId, &Information);
353
354 //
355 // Check for error
356 //
357 if (Status != ESUCCESS || Information.EndingAddress.HighPart != 0)
358 return 0;
359
360 //
361 // Return file size
362 //
363 return Information.EndingAddress.LowPart;
364 }
365
366 VOID FsSetFilePointer(PFILE FileHandle, ULONG NewFilePointer)
367 {
368 ULONG FileId = (ULONG)FileHandle;
369 LARGE_INTEGER Position;
370
371 //
372 // Set file position. Do not check for error,
373 // this function is supposed to always succeed.
374 //
375 Position.HighPart = 0;
376 Position.LowPart = NewFilePointer;
377 ArcSeek(FileId, &Position, SeekAbsolute);
378 }
379
380 /*
381 * FsGetNumPathParts()
382 * This function parses a path in the form of dir1\dir2\file1.ext
383 * and returns the number of parts it has (i.e. 3 - dir1,dir2,file1.ext)
384 */
385 ULONG FsGetNumPathParts(PCSTR Path)
386 {
387 size_t i;
388 ULONG num;
389
390 for (i = 0, num = 0; i < strlen(Path); i++)
391 {
392 if ((Path[i] == '\\') || (Path[i] == '/'))
393 {
394 num++;
395 }
396 }
397 num++;
398
399 TRACE("FsGetNumPathParts() Path = %s NumPathParts = %d\n", Path, num);
400
401 return num;
402 }
403
404 /*
405 * FsGetFirstNameFromPath()
406 * This function parses a path in the form of dir1\dir2\file1.ext
407 * and puts the first name of the path (e.g. "dir1") in buffer
408 * compatible with the MSDOS directory structure
409 */
410 VOID FsGetFirstNameFromPath(PCHAR Buffer, PCSTR Path)
411 {
412 size_t i;
413
414 // Copy all the characters up to the end of the
415 // string or until we hit a '\' character
416 // and put them in Buffer
417 for (i = 0; i < strlen(Path); i++)
418 {
419 if ((Path[i] == '\\') || (Path[i] == '/'))
420 {
421 break;
422 }
423 else
424 {
425 Buffer[i] = Path[i];
426 }
427 }
428
429 Buffer[i] = 0;
430
431 TRACE("FsGetFirstNameFromPath() Path = %s FirstName = %s\n", Path, Buffer);
432 }
433
434 VOID FsRegisterDevice(CHAR* Prefix, const DEVVTBL* FuncTable)
435 {
436 DEVICE* pNewEntry;
437 SIZE_T Length;
438
439 TRACE("FsRegisterDevice() Prefix = %s\n", Prefix);
440
441 Length = strlen(Prefix) + 1;
442 pNewEntry = FrLdrTempAlloc(sizeof(DEVICE) + Length, TAG_DEVICE);
443 if (!pNewEntry)
444 return;
445 pNewEntry->FuncTable = FuncTable;
446 pNewEntry->ReferenceCount = 0;
447 pNewEntry->Prefix = (CHAR*)(pNewEntry + 1);
448 memcpy(pNewEntry->Prefix, Prefix, Length);
449
450 InsertHeadList(&DeviceListHead, &pNewEntry->ListEntry);
451 }
452
453 LPCWSTR FsGetServiceName(ULONG FileId)
454 {
455 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
456 return NULL;
457 return FileData[FileId].FuncTable->ServiceName;
458 }
459
460 VOID FsSetDeviceSpecific(ULONG FileId, VOID* Specific)
461 {
462 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
463 return;
464 FileData[FileId].Specific = Specific;
465 }
466
467 VOID* FsGetDeviceSpecific(ULONG FileId)
468 {
469 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
470 return NULL;
471 return FileData[FileId].Specific;
472 }
473
474 ULONG FsGetDeviceId(ULONG FileId)
475 {
476 if (FileId >= MAX_FDS)
477 return (ULONG)-1;
478 return FileData[FileId].DeviceId;
479 }
480
481 VOID FsInit(VOID)
482 {
483 ULONG i;
484
485 RtlZeroMemory(FileData, sizeof(FileData));
486 for (i = 0; i < MAX_FDS; i++)
487 FileData[i].DeviceId = (ULONG)-1;
488
489 InitializeListHead(&DeviceListHead);
490
491 // FIXME: Retrieve the current boot device with MachDiskGetBootPath
492 // and store it somewhere in order to not call again and again this
493 // function.
494 }