3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 * Copyright (C) 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.
25 DBG_DEFAULT_CHANNEL(FILESYSTEM
);
27 #define SECTORSIZE 2048
28 #define TAG_ISO_BUFFER 'BosI'
29 #define TAG_ISO_FILE 'FosI'
31 static BOOLEAN
IsoSearchDirectoryBufferForFile(PVOID DirectoryBuffer
, ULONG DirectoryLength
, PCHAR FileName
, PISO_FILE_INFO IsoFileInfoPointer
)
38 TRACE("IsoSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x DirectoryLength = %d FileName = %s\n", DirectoryBuffer
, DirectoryLength
, FileName
);
40 RtlZeroMemory(Name
, 32 * sizeof(UCHAR
));
43 Record
= (PDIR_RECORD
)DirectoryBuffer
;
46 Offset
= Offset
+ Record
->RecordLength
;
47 Record
= (PDIR_RECORD
)((ULONG_PTR
)DirectoryBuffer
+ Offset
);
49 if (Record
->RecordLength
== 0)
51 Offset
= ROUND_UP(Offset
, SECTORSIZE
);
52 Record
= (PDIR_RECORD
)((ULONG_PTR
)DirectoryBuffer
+ Offset
);
55 if (Offset
>= DirectoryLength
)
58 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
62 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
68 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
69 Name
[i
] = Record
->FileId
[i
];
71 TRACE("Name '%s'\n", Name
);
73 if (strlen(FileName
) == strlen(Name
) && _stricmp(FileName
, Name
) == 0)
75 IsoFileInfoPointer
->FileStart
= Record
->ExtentLocationL
;
76 IsoFileInfoPointer
->FileSize
= Record
->DataLengthL
;
77 IsoFileInfoPointer
->FilePointer
= 0;
78 IsoFileInfoPointer
->Directory
= (Record
->FileFlags
& 0x02)?TRUE
:FALSE
;
85 RtlZeroMemory(Name
, 32 * sizeof(UCHAR
));
93 * IsoBufferDirectory()
94 * This function allocates a buffer, reads the specified directory
95 * and returns a pointer to that buffer into pDirectoryBuffer. The
96 * function returns an ARC error code. The directory is specified
97 * by its starting sector and length.
99 static ARC_STATUS
IsoBufferDirectory(ULONG DeviceId
, ULONG DirectoryStartSector
, ULONG DirectoryLength
,
100 PVOID
* pDirectoryBuffer
)
102 PVOID DirectoryBuffer
;
104 LARGE_INTEGER Position
;
108 TRACE("IsoBufferDirectory() DirectoryStartSector = %d DirectoryLength = %d\n", DirectoryStartSector
, DirectoryLength
);
110 SectorCount
= ROUND_UP(DirectoryLength
, SECTORSIZE
) / SECTORSIZE
;
111 TRACE("Trying to read (DirectoryCount) %d sectors.\n", SectorCount
);
114 // Attempt to allocate memory for directory buffer
116 TRACE("Trying to allocate (DirectoryLength) %d bytes.\n", DirectoryLength
);
117 DirectoryBuffer
= FrLdrTempAlloc(DirectoryLength
, TAG_ISO_BUFFER
);
118 if (!DirectoryBuffer
)
122 // Now read directory contents into DirectoryBuffer
124 Position
.HighPart
= 0;
125 Position
.LowPart
= DirectoryStartSector
* SECTORSIZE
;
126 Status
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
127 if (Status
!= ESUCCESS
)
129 FrLdrTempFree(DirectoryBuffer
, TAG_ISO_BUFFER
);
132 Status
= ArcRead(DeviceId
, DirectoryBuffer
, SectorCount
* SECTORSIZE
, &Count
);
133 if (Status
!= ESUCCESS
|| Count
!= SectorCount
* SECTORSIZE
)
135 FrLdrTempFree(DirectoryBuffer
, TAG_ISO_BUFFER
);
139 *pDirectoryBuffer
= DirectoryBuffer
;
146 * This function searches the file system for the
147 * specified filename and fills in an ISO_FILE_INFO structure
148 * with info describing the file, etc. returns ARC error code
150 static ARC_STATUS
IsoLookupFile(PCSTR FileName
, ULONG DeviceId
, PISO_FILE_INFO IsoFileInfoPointer
)
152 UCHAR Buffer
[SECTORSIZE
];
153 PPVD Pvd
= (PPVD
)Buffer
;
155 ULONG NumberOfPathParts
;
157 PVOID DirectoryBuffer
;
158 ULONG DirectorySector
;
159 ULONG DirectoryLength
;
160 ISO_FILE_INFO IsoFileInfo
;
161 LARGE_INTEGER Position
;
165 TRACE("IsoLookupFile() FileName = %s\n", FileName
);
167 RtlZeroMemory(IsoFileInfoPointer
, sizeof(ISO_FILE_INFO
));
168 RtlZeroMemory(&IsoFileInfo
, sizeof(ISO_FILE_INFO
));
171 // Read The Primary Volume Descriptor
173 Position
.HighPart
= 0;
174 Position
.LowPart
= 16 * SECTORSIZE
;
175 Status
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
176 if (Status
!= ESUCCESS
)
178 Status
= ArcRead(DeviceId
, Pvd
, SECTORSIZE
, &Count
);
179 if (Status
!= ESUCCESS
|| Count
< sizeof(PVD
))
182 DirectorySector
= Pvd
->RootDirRecord
.ExtentLocationL
;
183 DirectoryLength
= Pvd
->RootDirRecord
.DataLengthL
;
186 // Figure out how many sub-directories we are nested in
188 NumberOfPathParts
= FsGetNumPathParts(FileName
);
191 // Loop once for each part
193 for (i
=0; i
<NumberOfPathParts
; i
++)
196 // Get first path part
198 FsGetFirstNameFromPath(PathPart
, FileName
);
201 // Advance to the next part of the path
203 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
209 // Buffer the directory contents
211 Status
= IsoBufferDirectory(DeviceId
, DirectorySector
, DirectoryLength
, &DirectoryBuffer
);
212 if (Status
!= ESUCCESS
)
216 // Search for file name in directory
218 if (!IsoSearchDirectoryBufferForFile(DirectoryBuffer
, DirectoryLength
, PathPart
, &IsoFileInfo
))
220 FrLdrTempFree(DirectoryBuffer
, TAG_ISO_BUFFER
);
224 FrLdrTempFree(DirectoryBuffer
, TAG_ISO_BUFFER
);
227 // If we have another sub-directory to go then
228 // grab the start sector and file size
230 if ((i
+1) < NumberOfPathParts
)
232 DirectorySector
= IsoFileInfo
.FileStart
;
233 DirectoryLength
= IsoFileInfo
.FileSize
;
238 RtlCopyMemory(IsoFileInfoPointer
, &IsoFileInfo
, sizeof(ISO_FILE_INFO
));
243 ARC_STATUS
IsoClose(ULONG FileId
)
245 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
247 FrLdrTempFree(FileHandle
, TAG_ISO_FILE
);
252 ARC_STATUS
IsoGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
254 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
256 RtlZeroMemory(Information
, sizeof(*Information
));
257 Information
->EndingAddress
.LowPart
= FileHandle
->FileSize
;
258 Information
->CurrentAddress
.LowPart
= FileHandle
->FilePointer
;
260 TRACE("IsoGetFileInformation(%lu) -> FileSize = %lu, FilePointer = 0x%lx\n",
261 FileId
, Information
->EndingAddress
.LowPart
, Information
->CurrentAddress
.LowPart
);
266 ARC_STATUS
IsoOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
268 ISO_FILE_INFO TempFileInfo
;
269 PISO_FILE_INFO FileHandle
;
273 if (OpenMode
!= OpenReadOnly
)
276 DeviceId
= FsGetDeviceId(*FileId
);
278 TRACE("IsoOpen() FileName = %s\n", Path
);
280 RtlZeroMemory(&TempFileInfo
, sizeof(TempFileInfo
));
281 Status
= IsoLookupFile(Path
, DeviceId
, &TempFileInfo
);
282 if (Status
!= ESUCCESS
)
285 FileHandle
= FrLdrTempAlloc(sizeof(ISO_FILE_INFO
), TAG_ISO_FILE
);
289 RtlCopyMemory(FileHandle
, &TempFileInfo
, sizeof(ISO_FILE_INFO
));
291 FsSetDeviceSpecific(*FileId
, FileHandle
);
295 ARC_STATUS
IsoRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
297 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
298 UCHAR SectorBuffer
[SECTORSIZE
];
299 LARGE_INTEGER Position
;
303 ULONG OffsetInSector
;
304 ULONG LengthInSector
;
305 ULONG NumberOfSectors
;
309 TRACE("IsoRead() Buffer = %p, N = %lu\n", Buffer
, N
);
311 DeviceId
= FsGetDeviceId(FileId
);
315 // If they are trying to read past the
316 // end of the file then return success
319 FilePointer
= FileHandle
->FilePointer
;
320 if (FilePointer
>= FileHandle
->FileSize
)
326 // If they are trying to read more than there is to read
327 // then adjust the amount to read
329 if (FilePointer
+ N
> FileHandle
->FileSize
)
331 N
= FileHandle
->FileSize
- FilePointer
;
335 // Ok, now we have to perform at most 3 calculations
336 // I'll draw you a picture (using nifty ASCII art):
338 // CurrentFilePointer -+
340 // +----------------+
342 // +-----------+-----------+-----------+-----------+
343 // | Sector 1 | Sector 2 | Sector 3 | Sector 4 |
344 // +-----------+-----------+-----------+-----------+
346 // +---------------+--------------------+
348 // N -----------------+
350 // 1 - The first calculation (and read) will align
351 // the file pointer with the next sector
352 // boundary (if we are supposed to read that much)
353 // 2 - The next calculation (and read) will read
354 // in all the full sectors that the requested
355 // amount of data would cover (in this case
357 // 3 - The last calculation (and read) would read
358 // in the remainder of the data requested out of
364 // Only do the first read if we
365 // aren't aligned on a cluster boundary
367 if (FilePointer
% SECTORSIZE
)
370 // Do the math for our first read
372 SectorNumber
= FileHandle
->FileStart
+ (FilePointer
/ SECTORSIZE
);
373 OffsetInSector
= FilePointer
% SECTORSIZE
;
374 LengthInSector
= (N
> (SECTORSIZE
- OffsetInSector
)) ? (SECTORSIZE
- OffsetInSector
) : N
;
377 // Now do the read and update Count, N, FilePointer, & Buffer
379 Position
.HighPart
= 0;
380 Position
.LowPart
= SectorNumber
* SECTORSIZE
;
381 Status
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
382 if (Status
!= ESUCCESS
)
386 Status
= ArcRead(DeviceId
, SectorBuffer
, SECTORSIZE
, &BytesRead
);
387 if (Status
!= ESUCCESS
|| BytesRead
!= SECTORSIZE
)
391 RtlCopyMemory(Buffer
, SectorBuffer
+ OffsetInSector
, LengthInSector
);
392 *Count
+= LengthInSector
;
394 FilePointer
+= LengthInSector
;
395 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ LengthInSector
);
399 // Do the math for our second read (if any data left)
404 // Determine how many full clusters we need to read
406 NumberOfSectors
= (N
/ SECTORSIZE
);
408 SectorNumber
= FileHandle
->FileStart
+ (FilePointer
/ SECTORSIZE
);
411 // Now do the read and update Count, N, FilePointer, & Buffer
413 Position
.HighPart
= 0;
414 Position
.LowPart
= SectorNumber
* SECTORSIZE
;
415 Status
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
416 if (Status
!= ESUCCESS
)
420 Status
= ArcRead(DeviceId
, Buffer
, NumberOfSectors
* SECTORSIZE
, &BytesRead
);
421 if (Status
!= ESUCCESS
|| BytesRead
!= NumberOfSectors
* SECTORSIZE
)
426 *Count
+= NumberOfSectors
* SECTORSIZE
;
427 N
-= NumberOfSectors
* SECTORSIZE
;
428 FilePointer
+= NumberOfSectors
* SECTORSIZE
;
429 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ NumberOfSectors
* SECTORSIZE
);
433 // Do the math for our third read (if any data left)
437 SectorNumber
= FileHandle
->FileStart
+ (FilePointer
/ SECTORSIZE
);
440 // Now do the read and update Count, N, FilePointer, & Buffer
442 Position
.HighPart
= 0;
443 Position
.LowPart
= SectorNumber
* SECTORSIZE
;
444 Status
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
445 if (Status
!= ESUCCESS
)
449 Status
= ArcRead(DeviceId
, SectorBuffer
, SECTORSIZE
, &BytesRead
);
450 if (Status
!= ESUCCESS
|| BytesRead
!= SECTORSIZE
)
454 RtlCopyMemory(Buffer
, SectorBuffer
, N
);
459 TRACE("IsoRead() done\n");
464 ARC_STATUS
IsoSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
466 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
468 TRACE("IsoSeek() NewFilePointer = %lu\n", Position
->LowPart
);
470 if (SeekMode
!= SeekAbsolute
)
472 if (Position
->HighPart
!= 0)
474 if (Position
->LowPart
>= FileHandle
->FileSize
)
477 FileHandle
->FilePointer
= Position
->LowPart
;
481 const DEVVTBL Iso9660FuncTable
=
484 IsoGetFileInformation
,
491 const DEVVTBL
* IsoMount(ULONG DeviceId
)
493 UCHAR Buffer
[SECTORSIZE
];
494 PPVD Pvd
= (PPVD
)Buffer
;
495 LARGE_INTEGER Position
;
499 TRACE("Enter IsoMount(%lu)\n", DeviceId
);
502 // Read The Primary Volume Descriptor
504 Position
.HighPart
= 0;
505 Position
.LowPart
= 16 * SECTORSIZE
;
506 Status
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
507 if (Status
!= ESUCCESS
)
509 Status
= ArcRead(DeviceId
, Pvd
, SECTORSIZE
, &Count
);
510 if (Status
!= ESUCCESS
|| Count
< sizeof(PVD
))
514 // Check if PVD is valid. If yes, return ISO9660 function table
516 if (Pvd
->VdType
== 1 && RtlEqualMemory(Pvd
->StandardId
, "CD001", 5))
518 TRACE("IsoMount(%lu) success\n", DeviceId
);
519 return &Iso9660FuncTable
;