[FORMATTING] Remove trailing whitespace. Addendum to 34593d93.
[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 DBG_DEFAULT_CHANNEL(FILESYSTEM);
27
28 /* GLOBALS ********************************************************************/
29
30 #define TAG_DEVICE_NAME 'NDsF'
31 #define TAG_DEVICE 'vDsF'
32
33 typedef struct tagFILEDATA
34 {
35 ULONG DeviceId;
36 ULONG ReferenceCount;
37 const DEVVTBL* FuncTable;
38 const DEVVTBL* FileFuncTable;
39 VOID* Specific;
40 } FILEDATA;
41
42 typedef struct tagDEVICE
43 {
44 LIST_ENTRY ListEntry;
45 const DEVVTBL* FuncTable;
46 CHAR* Prefix;
47 ULONG DeviceId;
48 ULONG ReferenceCount;
49 } DEVICE;
50
51 static FILEDATA FileData[MAX_FDS];
52 static LIST_ENTRY DeviceListHead;
53
54 /* ARC FUNCTIONS **************************************************************/
55
56 ARC_STATUS ArcOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
57 {
58 ARC_STATUS Status;
59 ULONG Count, i;
60 PLIST_ENTRY pEntry;
61 DEVICE* pDevice;
62 CHAR* DeviceName;
63 CHAR* FileName;
64 CHAR* p;
65 CHAR* q;
66 SIZE_T Length;
67 OPENMODE DeviceOpenMode;
68 ULONG DeviceId;
69
70 /* Print status message */
71 TRACE("Opening file '%s'...\n", Path);
72
73 *FileId = MAX_FDS;
74
75 /* Search last ')', which delimits device and path */
76 FileName = strrchr(Path, ')');
77 if (!FileName)
78 return EINVAL;
79 FileName++;
80
81 /* Count number of "()", which needs to be replaced by "(0)" */
82 Count = 0;
83 for (p = Path; p != FileName; p++)
84 {
85 if (*p == '(' && *(p + 1) == ')')
86 Count++;
87 }
88
89 /* Duplicate device name, and replace "()" by "(0)" (if required) */
90 Length = FileName - Path + Count;
91 if (Count != 0)
92 {
93 DeviceName = FrLdrTempAlloc(FileName - Path + Count, TAG_DEVICE_NAME);
94 if (!DeviceName)
95 return ENOMEM;
96 for (p = Path, q = DeviceName; p != FileName; p++)
97 {
98 *q++ = *p;
99 if (*p == '(' && *(p + 1) == ')')
100 *q++ = '0';
101 }
102 }
103 else
104 {
105 DeviceName = Path;
106 }
107
108 /* Search for the device */
109 if (OpenMode == OpenReadOnly || OpenMode == OpenWriteOnly)
110 DeviceOpenMode = OpenMode;
111 else
112 DeviceOpenMode = OpenReadWrite;
113
114 pEntry = DeviceListHead.Flink;
115 while (pEntry != &DeviceListHead)
116 {
117 pDevice = CONTAINING_RECORD(pEntry, DEVICE, ListEntry);
118 if (strncmp(pDevice->Prefix, DeviceName, Length) == 0)
119 {
120 /* OK, device found. It is already opened? */
121 if (pDevice->ReferenceCount == 0)
122 {
123 /* Search some room for the device */
124 for (DeviceId = 0; DeviceId < MAX_FDS; DeviceId++)
125 {
126 if (!FileData[DeviceId].FuncTable)
127 break;
128 }
129 if (DeviceId == MAX_FDS)
130 return EMFILE;
131
132 /* Try to open the device */
133 FileData[DeviceId].FuncTable = pDevice->FuncTable;
134 Status = pDevice->FuncTable->Open(pDevice->Prefix, DeviceOpenMode, &DeviceId);
135 if (Status != ESUCCESS)
136 {
137 FileData[DeviceId].FuncTable = NULL;
138 return Status;
139 }
140 else if (!*FileName)
141 {
142 /* Done, caller wanted to open the raw device */
143 *FileId = DeviceId;
144 pDevice->ReferenceCount++;
145 return ESUCCESS;
146 }
147
148 /* Try to detect the file system */
149 #ifndef _M_ARM
150 FileData[DeviceId].FileFuncTable = IsoMount(DeviceId);
151 if (!FileData[DeviceId].FileFuncTable)
152 #endif
153 FileData[DeviceId].FileFuncTable = FatMount(DeviceId);
154 if (!FileData[DeviceId].FileFuncTable)
155 FileData[DeviceId].FileFuncTable = BtrFsMount(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 path separator, if any */
202 if (*FileName == '\\' || *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 ARC_STATUS
266 FsOpenFile(
267 IN PCSTR FileName,
268 IN PCSTR DefaultPath OPTIONAL,
269 IN OPENMODE OpenMode,
270 OUT PULONG FileId)
271 {
272 NTSTATUS Status;
273 SIZE_T cchPathLen;
274 CHAR FullPath[MAX_PATH] = "";
275
276 /*
277 * Check whether FileName is a full path and if not, create a full
278 * file name using the user-provided default path (if present).
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: prepend the user-provided default path */
285 if (DefaultPath)
286 {
287 Status = RtlStringCbCopyA(FullPath, sizeof(FullPath), DefaultPath);
288 if (!NT_SUCCESS(Status))
289 return ENAMETOOLONG;
290 }
291
292 /* Append a path separator if needed */
293
294 cchPathLen = min(sizeof(FullPath)/sizeof(CHAR), strlen(FullPath));
295 if (cchPathLen >= sizeof(FullPath)/sizeof(CHAR))
296 return ENAMETOOLONG;
297
298 if ((*FileName != '\\' && *FileName != '/') &&
299 cchPathLen > 0 && (FullPath[cchPathLen-1] != '\\' && FullPath[cchPathLen-1] != '/'))
300 {
301 /* FileName does not start with '\' and FullPath does not end with '\' */
302 Status = RtlStringCbCatA(FullPath, sizeof(FullPath), "\\");
303 if (!NT_SUCCESS(Status))
304 return ENAMETOOLONG;
305 }
306 else if ((*FileName == '\\' || *FileName == '/') &&
307 cchPathLen > 0 && (FullPath[cchPathLen-1] == '\\' || FullPath[cchPathLen-1] == '/'))
308 {
309 /* FileName starts with '\' and FullPath ends with '\' */
310 while (*FileName == '\\' || *FileName == '/')
311 ++FileName; // Skip any backslash
312 }
313 }
314 /* Append (or just copy) the remaining file name */
315 Status = RtlStringCbCatA(FullPath, sizeof(FullPath), FileName);
316 if (!NT_SUCCESS(Status))
317 return ENAMETOOLONG;
318
319 /* Open the file */
320 return ArcOpen(FullPath, OpenMode, FileId);
321 }
322
323 /*
324 * FsGetNumPathParts()
325 * This function parses a path in the form of dir1\dir2\file1.ext
326 * and returns the number of parts it has (i.e. 3 - dir1,dir2,file1.ext)
327 */
328 ULONG FsGetNumPathParts(PCSTR Path)
329 {
330 size_t i;
331 size_t len;
332 ULONG num;
333
334 len = strlen(Path);
335
336 for (i = 0, num = 0; i < len; i++)
337 {
338 if ((Path[i] == '\\') || (Path[i] == '/'))
339 {
340 num++;
341 }
342 }
343 num++;
344
345 TRACE("FsGetNumPathParts() Path = %s NumPathParts = %d\n", Path, num);
346
347 return num;
348 }
349
350 /*
351 * FsGetFirstNameFromPath()
352 * This function parses a path in the form of dir1\dir2\file1.ext
353 * and puts the first name of the path (e.g. "dir1") in buffer
354 * compatible with the MSDOS directory structure
355 */
356 VOID FsGetFirstNameFromPath(PCHAR Buffer, PCSTR Path)
357 {
358 size_t i;
359 size_t len;
360
361 len = strlen(Path);
362
363 // Copy all the characters up to the end of the
364 // string or until we hit a '\' character
365 // and put them in Buffer
366 for (i = 0; i < len; i++)
367 {
368 if ((Path[i] == '\\') || (Path[i] == '/'))
369 {
370 break;
371 }
372 else
373 {
374 Buffer[i] = Path[i];
375 }
376 }
377
378 Buffer[i] = 0;
379
380 TRACE("FsGetFirstNameFromPath() Path = %s FirstName = %s\n", Path, Buffer);
381 }
382
383 VOID FsRegisterDevice(CHAR* Prefix, const DEVVTBL* FuncTable)
384 {
385 DEVICE* pNewEntry;
386 SIZE_T Length;
387
388 TRACE("FsRegisterDevice() Prefix = %s\n", Prefix);
389
390 Length = strlen(Prefix) + 1;
391 pNewEntry = FrLdrTempAlloc(sizeof(DEVICE) + Length, TAG_DEVICE);
392 if (!pNewEntry)
393 return;
394 pNewEntry->FuncTable = FuncTable;
395 pNewEntry->ReferenceCount = 0;
396 pNewEntry->Prefix = (CHAR*)(pNewEntry + 1);
397 RtlCopyMemory(pNewEntry->Prefix, Prefix, Length);
398
399 InsertHeadList(&DeviceListHead, &pNewEntry->ListEntry);
400 }
401
402 PCWSTR FsGetServiceName(ULONG FileId)
403 {
404 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
405 return NULL;
406 return FileData[FileId].FuncTable->ServiceName;
407 }
408
409 VOID FsSetDeviceSpecific(ULONG FileId, VOID* Specific)
410 {
411 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
412 return;
413 FileData[FileId].Specific = Specific;
414 }
415
416 VOID* FsGetDeviceSpecific(ULONG FileId)
417 {
418 if (FileId >= MAX_FDS || !FileData[FileId].FuncTable)
419 return NULL;
420 return FileData[FileId].Specific;
421 }
422
423 ULONG FsGetDeviceId(ULONG FileId)
424 {
425 if (FileId >= MAX_FDS)
426 return (ULONG)-1;
427 return FileData[FileId].DeviceId;
428 }
429
430 VOID FsInit(VOID)
431 {
432 ULONG i;
433
434 RtlZeroMemory(FileData, sizeof(FileData));
435 for (i = 0; i < MAX_FDS; i++)
436 FileData[i].DeviceId = (ULONG)-1;
437
438 InitializeListHead(&DeviceListHead);
439 }