3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 * Copyright (C) 2008-2009 Hervé Poussineau <hpoussin@reactos.org>
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.
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.
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.
21 /* INCLUDES *******************************************************************/
28 DBG_DEFAULT_CHANNEL(FILESYSTEM
);
30 /* GLOBALS ********************************************************************/
32 #define TAG_DEVICE_NAME 'NDsF'
33 #define TAG_DEVICE 'vDsF'
35 typedef struct tagFILEDATA
39 const DEVVTBL
* FuncTable
;
40 const DEVVTBL
* FileFuncTable
;
44 typedef struct tagDEVICE
47 const DEVVTBL
* FuncTable
;
53 static FILEDATA FileData
[MAX_FDS
];
54 static LIST_ENTRY DeviceListHead
;
56 /* ARC FUNCTIONS **************************************************************/
58 ARC_STATUS
ArcOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
69 OPENMODE DeviceOpenMode
;
72 /* Print status message */
73 TRACE("Opening file '%s'...\n", Path
);
77 /* Search last ')', which delimits device and path */
78 FileName
= strrchr(Path
, ')');
83 /* Count number of "()", which needs to be replaced by "(0)" */
85 for (p
= Path
; p
!= FileName
; p
++)
87 if (*p
== '(' && *(p
+ 1) == ')')
91 /* Duplicate device name, and replace "()" by "(0)" (if required) */
92 Length
= FileName
- Path
+ Count
;
95 DeviceName
= FrLdrTempAlloc(FileName
- Path
+ Count
, TAG_DEVICE_NAME
);
98 for (p
= Path
, q
= DeviceName
; p
!= FileName
; p
++)
101 if (*p
== '(' && *(p
+ 1) == ')')
110 /* Search for the device */
111 if (OpenMode
== OpenReadOnly
|| OpenMode
== OpenWriteOnly
)
112 DeviceOpenMode
= OpenMode
;
114 DeviceOpenMode
= OpenReadWrite
;
116 pEntry
= DeviceListHead
.Flink
;
117 while (pEntry
!= &DeviceListHead
)
119 pDevice
= CONTAINING_RECORD(pEntry
, DEVICE
, ListEntry
);
120 if (strncmp(pDevice
->Prefix
, DeviceName
, Length
) == 0)
122 /* OK, device found. It is already opened? */
123 if (pDevice
->ReferenceCount
== 0)
125 /* Search some room for the device */
126 for (DeviceId
= 0; DeviceId
< MAX_FDS
; DeviceId
++)
128 if (!FileData
[DeviceId
].FuncTable
)
131 if (DeviceId
== MAX_FDS
)
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
)
139 FileData
[DeviceId
].FuncTable
= NULL
;
144 /* Done, caller wanted to open the raw device */
146 pDevice
->ReferenceCount
++;
150 /* Try to detect the file system */
152 FileData
[DeviceId
].FileFuncTable
= IsoMount(DeviceId
);
153 if (!FileData
[DeviceId
].FileFuncTable
)
155 FileData
[DeviceId
].FileFuncTable
= FatMount(DeviceId
);
157 if (!FileData
[DeviceId
].FileFuncTable
)
158 FileData
[DeviceId
].FileFuncTable
= NtfsMount(DeviceId
);
159 if (!FileData
[DeviceId
].FileFuncTable
)
160 FileData
[DeviceId
].FileFuncTable
= Ext2Mount(DeviceId
);
162 #if defined(_M_IX86) || defined(_M_AMD64)
163 if (!FileData
[DeviceId
].FileFuncTable
)
164 FileData
[DeviceId
].FileFuncTable
= PxeMount(DeviceId
);
166 if (!FileData
[DeviceId
].FileFuncTable
)
168 /* Error, unable to detect file system */
169 pDevice
->FuncTable
->Close(DeviceId
);
170 FileData
[DeviceId
].FuncTable
= NULL
;
174 pDevice
->DeviceId
= DeviceId
;
178 DeviceId
= pDevice
->DeviceId
;
180 pDevice
->ReferenceCount
++;
183 pEntry
= pEntry
->Flink
;
185 if (pEntry
== &DeviceListHead
)
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 */
192 /* Search some room for the device */
193 for (i
= 0; i
< MAX_FDS
; i
++)
195 if (!FileData
[i
].FuncTable
)
201 /* Skip leading backslash, if any */
202 if (*FileName
== '\\')
206 FileData
[i
].FuncTable
= FileData
[DeviceId
].FileFuncTable
;
207 FileData
[i
].DeviceId
= DeviceId
;
209 Status
= FileData
[i
].FuncTable
->Open(FileName
, OpenMode
, FileId
);
210 if (Status
!= ESUCCESS
)
212 FileData
[i
].FuncTable
= NULL
;
218 ARC_STATUS
ArcClose(ULONG FileId
)
222 if (FileId
>= MAX_FDS
|| !FileData
[FileId
].FuncTable
)
225 Status
= FileData
[FileId
].FuncTable
->Close(FileId
);
227 if (Status
== ESUCCESS
)
229 FileData
[FileId
].FuncTable
= NULL
;
230 FileData
[FileId
].Specific
= NULL
;
231 FileData
[FileId
].DeviceId
= -1;
236 ARC_STATUS
ArcRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
238 if (FileId
>= MAX_FDS
|| !FileData
[FileId
].FuncTable
)
240 return FileData
[FileId
].FuncTable
->Read(FileId
, Buffer
, N
, Count
);
243 ARC_STATUS
ArcSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
245 if (FileId
>= MAX_FDS
|| !FileData
[FileId
].FuncTable
)
247 return FileData
[FileId
].FuncTable
->Seek(FileId
, Position
, SeekMode
);
250 ARC_STATUS
ArcGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
252 if (FileId
>= MAX_FDS
|| !FileData
[FileId
].FuncTable
)
254 return FileData
[FileId
].FuncTable
->GetFileInformation(FileId
, Information
);
257 /* FUNCTIONS ******************************************************************/
259 VOID
FileSystemError(PCSTR ErrorString
)
261 ERR("%s\n", ErrorString
);
262 UiMessageBox(ErrorString
);
265 PFILE
FsOpenFile(PCSTR FileName
)
267 CHAR FullPath
[MAX_PATH
] = "";
272 // Print status message
274 TRACE("Opening file '%s'...\n", FileName
);
277 // Check whether FileName is a full path
278 // and if not, create a full file name.
280 // See ArcOpen: Search last ')', which delimits device and path.
282 if (strrchr(FileName
, ')') == NULL
)
284 /* This is not a full path. Use the current (i.e. boot) device. */
285 MachDiskGetBootPath(FullPath
, sizeof(FullPath
));
287 /* Append a path separator if needed */
288 if (FileName
[0] != '\\' && FileName
[0] != '/')
289 strcat(FullPath
, "\\");
291 // Append (or just copy) the remaining file name.
292 strcat(FullPath
, FileName
);
297 Status
= ArcOpen(FullPath
, OpenReadOnly
, &FileId
);
302 if (Status
== ESUCCESS
)
303 return (PFILE
)FileId
;
308 VOID
FsCloseFile(PFILE FileHandle
)
310 ULONG FileId
= (ULONG
)FileHandle
;
313 // Close the handle. Do not check for error,
314 // this function is supposed to always succeed.
321 * returns number of bytes read or EOF
323 BOOLEAN
FsReadFile(PFILE FileHandle
, ULONG BytesToRead
, ULONG
* BytesRead
, PVOID Buffer
)
325 ULONG FileId
= (ULONG
)FileHandle
;
330 return (ArcRead(FileId
, Buffer
, BytesToRead
, BytesRead
) == ESUCCESS
);
333 BOOLEAN
FsGetFileInformation(PFILE FileHandle
, FILEINFORMATION
* Information
)
335 ULONG FileId
= (ULONG
)FileHandle
;
338 // Get file information
340 return (ArcGetFileInformation(FileId
, Information
) == ESUCCESS
);
343 ULONG
FsGetFileSize(PFILE FileHandle
)
345 ULONG FileId
= (ULONG
)FileHandle
;
346 FILEINFORMATION Information
;
350 // Query file informations
352 Status
= ArcGetFileInformation(FileId
, &Information
);
357 if (Status
!= ESUCCESS
|| Information
.EndingAddress
.HighPart
!= 0)
363 return Information
.EndingAddress
.LowPart
;
366 VOID
FsSetFilePointer(PFILE FileHandle
, ULONG NewFilePointer
)
368 ULONG FileId
= (ULONG
)FileHandle
;
369 LARGE_INTEGER Position
;
372 // Set file position. Do not check for error,
373 // this function is supposed to always succeed.
375 Position
.HighPart
= 0;
376 Position
.LowPart
= NewFilePointer
;
377 ArcSeek(FileId
, &Position
, SeekAbsolute
);
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)
385 ULONG
FsGetNumPathParts(PCSTR Path
)
393 for (i
= 0, num
= 0; i
< len
; i
++)
395 if ((Path
[i
] == '\\') || (Path
[i
] == '/'))
402 TRACE("FsGetNumPathParts() Path = %s NumPathParts = %d\n", Path
, num
);
408 * FsGetFirstNameFromPath()
409 * This function parses a path in the form of dir1\dir2\file1.ext
410 * and puts the first name of the path (e.g. "dir1") in buffer
411 * compatible with the MSDOS directory structure
413 VOID
FsGetFirstNameFromPath(PCHAR Buffer
, PCSTR Path
)
420 // Copy all the characters up to the end of the
421 // string or until we hit a '\' character
422 // and put them in Buffer
423 for (i
= 0; i
< len
; i
++)
425 if ((Path
[i
] == '\\') || (Path
[i
] == '/'))
437 TRACE("FsGetFirstNameFromPath() Path = %s FirstName = %s\n", Path
, Buffer
);
440 VOID
FsRegisterDevice(CHAR
* Prefix
, const DEVVTBL
* FuncTable
)
445 TRACE("FsRegisterDevice() Prefix = %s\n", Prefix
);
447 Length
= strlen(Prefix
) + 1;
448 pNewEntry
= FrLdrTempAlloc(sizeof(DEVICE
) + Length
, TAG_DEVICE
);
451 pNewEntry
->FuncTable
= FuncTable
;
452 pNewEntry
->ReferenceCount
= 0;
453 pNewEntry
->Prefix
= (CHAR
*)(pNewEntry
+ 1);
454 memcpy(pNewEntry
->Prefix
, Prefix
, Length
);
456 InsertHeadList(&DeviceListHead
, &pNewEntry
->ListEntry
);
459 LPCWSTR
FsGetServiceName(ULONG FileId
)
461 if (FileId
>= MAX_FDS
|| !FileData
[FileId
].FuncTable
)
463 return FileData
[FileId
].FuncTable
->ServiceName
;
466 VOID
FsSetDeviceSpecific(ULONG FileId
, VOID
* Specific
)
468 if (FileId
>= MAX_FDS
|| !FileData
[FileId
].FuncTable
)
470 FileData
[FileId
].Specific
= Specific
;
473 VOID
* FsGetDeviceSpecific(ULONG FileId
)
475 if (FileId
>= MAX_FDS
|| !FileData
[FileId
].FuncTable
)
477 return FileData
[FileId
].Specific
;
480 ULONG
FsGetDeviceId(ULONG FileId
)
482 if (FileId
>= MAX_FDS
)
484 return FileData
[FileId
].DeviceId
;
491 RtlZeroMemory(FileData
, sizeof(FileData
));
492 for (i
= 0; i
< MAX_FDS
; i
++)
493 FileData
[i
].DeviceId
= (ULONG
)-1;
495 InitializeListHead(&DeviceListHead
);
497 // FIXME: Retrieve the current boot device with MachDiskGetBootPath
498 // and store it somewhere in order to not call again and again this