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