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 *******************************************************************/
26 DBG_DEFAULT_CHANNEL(FILESYSTEM
);
28 /* GLOBALS ********************************************************************/
30 #define TAG_DEVICE_NAME 'NDsF'
31 #define TAG_DEVICE 'vDsF'
33 typedef struct tagFILEDATA
37 const DEVVTBL
* FuncTable
;
38 const DEVVTBL
* FileFuncTable
;
42 typedef struct tagDEVICE
45 const DEVVTBL
* FuncTable
;
51 static FILEDATA FileData
[MAX_FDS
];
52 static LIST_ENTRY DeviceListHead
;
54 /* ARC FUNCTIONS **************************************************************/
56 ARC_STATUS
ArcOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
67 OPENMODE DeviceOpenMode
;
70 /* Print status message */
71 TRACE("Opening file '%s'...\n", Path
);
75 /* Search last ')', which delimits device and path */
76 FileName
= strrchr(Path
, ')');
81 /* Count number of "()", which needs to be replaced by "(0)" */
83 for (p
= Path
; p
!= FileName
; p
++)
85 if (*p
== '(' && *(p
+ 1) == ')')
89 /* Duplicate device name, and replace "()" by "(0)" (if required) */
90 Length
= FileName
- Path
+ Count
;
93 DeviceName
= FrLdrTempAlloc(FileName
- Path
+ Count
, TAG_DEVICE_NAME
);
96 for (p
= Path
, q
= DeviceName
; p
!= FileName
; p
++)
99 if (*p
== '(' && *(p
+ 1) == ')')
108 /* Search for the device */
109 if (OpenMode
== OpenReadOnly
|| OpenMode
== OpenWriteOnly
)
110 DeviceOpenMode
= OpenMode
;
112 DeviceOpenMode
= OpenReadWrite
;
114 pEntry
= DeviceListHead
.Flink
;
115 while (pEntry
!= &DeviceListHead
)
117 pDevice
= CONTAINING_RECORD(pEntry
, DEVICE
, ListEntry
);
118 if (strncmp(pDevice
->Prefix
, DeviceName
, Length
) == 0)
120 /* OK, device found. It is already opened? */
121 if (pDevice
->ReferenceCount
== 0)
123 /* Search some room for the device */
124 for (DeviceId
= 0; DeviceId
< MAX_FDS
; DeviceId
++)
126 if (!FileData
[DeviceId
].FuncTable
)
129 if (DeviceId
== MAX_FDS
)
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
)
137 FileData
[DeviceId
].FuncTable
= NULL
;
142 /* Done, caller wanted to open the raw device */
144 pDevice
->ReferenceCount
++;
148 /* Try to detect the file system */
150 FileData
[DeviceId
].FileFuncTable
= IsoMount(DeviceId
);
151 if (!FileData
[DeviceId
].FileFuncTable
)
153 FileData
[DeviceId
].FileFuncTable
= FatMount(DeviceId
);
154 if (!FileData
[DeviceId
].FileFuncTable
)
155 FileData
[DeviceId
].FileFuncTable
= BtrFsMount(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 path separator, if any */
202 if (*FileName
== '\\' || *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
);
268 IN PCSTR DefaultPath OPTIONAL
,
269 IN OPENMODE OpenMode
,
274 CHAR FullPath
[MAX_PATH
] = "";
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).
280 * See ArcOpen(): Search last ')', which delimits device and path.
282 if (strrchr(FileName
, ')') == NULL
)
284 /* This is not a full path: prepend the user-provided default path */
287 Status
= RtlStringCbCopyA(FullPath
, sizeof(FullPath
), DefaultPath
);
288 if (!NT_SUCCESS(Status
))
292 /* Append a path separator if needed */
294 cchPathLen
= min(sizeof(FullPath
)/sizeof(CHAR
), strlen(FullPath
));
295 if (cchPathLen
>= sizeof(FullPath
)/sizeof(CHAR
))
298 if ((*FileName
!= '\\' && *FileName
!= '/') &&
299 cchPathLen
> 0 && (FullPath
[cchPathLen
-1] != '\\' && FullPath
[cchPathLen
-1] != '/'))
301 /* FileName does not start with '\' and FullPath does not end with '\' */
302 Status
= RtlStringCbCatA(FullPath
, sizeof(FullPath
), "\\");
303 if (!NT_SUCCESS(Status
))
306 else if ((*FileName
== '\\' || *FileName
== '/') &&
307 cchPathLen
> 0 && (FullPath
[cchPathLen
-1] == '\\' || FullPath
[cchPathLen
-1] == '/'))
309 /* FileName starts with '\' and FullPath ends with '\' */
310 while (*FileName
== '\\' || *FileName
== '/')
311 ++FileName
; // Skip any backslash
314 /* Append (or just copy) the remaining file name */
315 Status
= RtlStringCbCatA(FullPath
, sizeof(FullPath
), FileName
);
316 if (!NT_SUCCESS(Status
))
320 return ArcOpen(FullPath
, OpenMode
, FileId
);
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)
328 ULONG
FsGetNumPathParts(PCSTR Path
)
336 for (i
= 0, num
= 0; i
< len
; i
++)
338 if ((Path
[i
] == '\\') || (Path
[i
] == '/'))
345 TRACE("FsGetNumPathParts() Path = %s NumPathParts = %d\n", Path
, num
);
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
356 VOID
FsGetFirstNameFromPath(PCHAR Buffer
, PCSTR Path
)
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
++)
368 if ((Path
[i
] == '\\') || (Path
[i
] == '/'))
380 TRACE("FsGetFirstNameFromPath() Path = %s FirstName = %s\n", Path
, Buffer
);
383 VOID
FsRegisterDevice(CHAR
* Prefix
, const DEVVTBL
* FuncTable
)
388 TRACE("FsRegisterDevice() Prefix = %s\n", Prefix
);
390 Length
= strlen(Prefix
) + 1;
391 pNewEntry
= FrLdrTempAlloc(sizeof(DEVICE
) + Length
, TAG_DEVICE
);
394 pNewEntry
->FuncTable
= FuncTable
;
395 pNewEntry
->ReferenceCount
= 0;
396 pNewEntry
->Prefix
= (CHAR
*)(pNewEntry
+ 1);
397 RtlCopyMemory(pNewEntry
->Prefix
, Prefix
, Length
);
399 InsertHeadList(&DeviceListHead
, &pNewEntry
->ListEntry
);
402 PCWSTR
FsGetServiceName(ULONG FileId
)
404 if (FileId
>= MAX_FDS
|| !FileData
[FileId
].FuncTable
)
406 return FileData
[FileId
].FuncTable
->ServiceName
;
409 VOID
FsSetDeviceSpecific(ULONG FileId
, VOID
* Specific
)
411 if (FileId
>= MAX_FDS
|| !FileData
[FileId
].FuncTable
)
413 FileData
[FileId
].Specific
= Specific
;
416 VOID
* FsGetDeviceSpecific(ULONG FileId
)
418 if (FileId
>= MAX_FDS
|| !FileData
[FileId
].FuncTable
)
420 return FileData
[FileId
].Specific
;
423 ULONG
FsGetDeviceId(ULONG FileId
)
425 if (FileId
>= MAX_FDS
)
427 return FileData
[FileId
].DeviceId
;
434 RtlZeroMemory(FileData
, sizeof(FileData
));
435 for (i
= 0; i
< MAX_FDS
; i
++)
436 FileData
[i
].DeviceId
= (ULONG
)-1;
438 InitializeListHead(&DeviceListHead
);