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.
24 #define SECTORSIZE 2048
26 static BOOLEAN
IsoSearchDirectoryBufferForFile(PVOID DirectoryBuffer
, ULONG DirectoryLength
, PCHAR FileName
, PISO_FILE_INFO IsoFileInfoPointer
)
33 DPRINTM(DPRINT_FILESYSTEM
, "IsoSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x DirectoryLength = %d FileName = %s\n", DirectoryBuffer
, DirectoryLength
, FileName
);
35 RtlZeroMemory(Name
, 32 * sizeof(UCHAR
));
38 Record
= (PDIR_RECORD
)DirectoryBuffer
;
41 Offset
= Offset
+ Record
->RecordLength
;
42 Record
= (PDIR_RECORD
)((ULONG_PTR
)DirectoryBuffer
+ Offset
);
44 if (Record
->RecordLength
== 0)
46 Offset
= ROUND_UP(Offset
, SECTORSIZE
);
47 Record
= (PDIR_RECORD
)((ULONG_PTR
)DirectoryBuffer
+ Offset
);
50 if (Offset
>= DirectoryLength
)
53 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
55 DPRINTM(DPRINT_FILESYSTEM
, "Name '.'\n");
57 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
59 DPRINTM(DPRINT_FILESYSTEM
, "Name '..'\n");
63 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
64 Name
[i
] = Record
->FileId
[i
];
66 DPRINTM(DPRINT_FILESYSTEM
, "Name '%s'\n", Name
);
68 if (strlen(FileName
) == strlen(Name
) && _stricmp(FileName
, Name
) == 0)
70 IsoFileInfoPointer
->FileStart
= Record
->ExtentLocationL
;
71 IsoFileInfoPointer
->FileSize
= Record
->DataLengthL
;
72 IsoFileInfoPointer
->FilePointer
= 0;
73 IsoFileInfoPointer
->Directory
= (Record
->FileFlags
& 0x02)?TRUE
:FALSE
;
80 RtlZeroMemory(Name
, 32 * sizeof(UCHAR
));
88 * IsoBufferDirectory()
89 * This function allocates a buffer, reads the specified directory
90 * and returns a pointer to that buffer into pDirectoryBuffer. The
91 * function returns an ARC error code. The directory is specified
92 * by its starting sector and length.
94 static LONG
IsoBufferDirectory(ULONG DeviceId
, ULONG DirectoryStartSector
, ULONG DirectoryLength
,
95 PVOID
* pDirectoryBuffer
)
97 PVOID DirectoryBuffer
;
99 LARGE_INTEGER Position
;
103 DPRINTM(DPRINT_FILESYSTEM
, "IsoBufferDirectory() DirectoryStartSector = %d DirectoryLength = %d\n", DirectoryStartSector
, DirectoryLength
);
105 SectorCount
= ROUND_UP(DirectoryLength
, SECTORSIZE
) / SECTORSIZE
;
106 DPRINTM(DPRINT_FILESYSTEM
, "Trying to read (DirectoryCount) %d sectors.\n", SectorCount
);
109 // Attempt to allocate memory for directory buffer
111 DPRINTM(DPRINT_FILESYSTEM
, "Trying to allocate (DirectoryLength) %d bytes.\n", DirectoryLength
);
112 DirectoryBuffer
= MmHeapAlloc(DirectoryLength
);
113 if (!DirectoryBuffer
)
117 // Now read directory contents into DirectoryBuffer
119 Position
.HighPart
= 0;
120 Position
.LowPart
= DirectoryStartSector
* SECTORSIZE
;
121 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
124 MmHeapFree(DirectoryBuffer
);
127 ret
= ArcRead(DeviceId
, DirectoryBuffer
, SectorCount
* SECTORSIZE
, &Count
);
128 if (ret
!= ESUCCESS
|| Count
!= SectorCount
* SECTORSIZE
)
130 MmHeapFree(DirectoryBuffer
);
134 *pDirectoryBuffer
= DirectoryBuffer
;
141 * This function searches the file system for the
142 * specified filename and fills in an ISO_FILE_INFO structure
143 * with info describing the file, etc. returns ARC error code
145 static LONG
IsoLookupFile(PCSTR FileName
, ULONG DeviceId
, PISO_FILE_INFO IsoFileInfoPointer
)
147 UCHAR Buffer
[SECTORSIZE
];
148 PPVD Pvd
= (PPVD
)Buffer
;
150 ULONG NumberOfPathParts
;
152 PVOID DirectoryBuffer
;
153 ULONG DirectorySector
;
154 ULONG DirectoryLength
;
155 ISO_FILE_INFO IsoFileInfo
;
156 LARGE_INTEGER Position
;
160 DPRINTM(DPRINT_FILESYSTEM
, "IsoLookupFile() FileName = %s\n", FileName
);
162 RtlZeroMemory(IsoFileInfoPointer
, sizeof(ISO_FILE_INFO
));
165 // Read The Primary Volume Descriptor
167 Position
.HighPart
= 0;
168 Position
.LowPart
= 16 * SECTORSIZE
;
169 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
172 ret
= ArcRead(DeviceId
, Pvd
, SECTORSIZE
, &Count
);
173 if (ret
!= ESUCCESS
|| Count
< sizeof(PVD
))
176 DirectorySector
= Pvd
->RootDirRecord
.ExtentLocationL
;
177 DirectoryLength
= Pvd
->RootDirRecord
.DataLengthL
;
180 // Figure out how many sub-directories we are nested in
182 NumberOfPathParts
= FsGetNumPathParts(FileName
);
185 // Loop once for each part
187 for (i
=0; i
<NumberOfPathParts
; i
++)
190 // Get first path part
192 FsGetFirstNameFromPath(PathPart
, FileName
);
195 // Advance to the next part of the path
197 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
203 // Buffer the directory contents
205 ret
= IsoBufferDirectory(DeviceId
, DirectorySector
, DirectoryLength
, &DirectoryBuffer
);
210 // Search for file name in directory
212 if (!IsoSearchDirectoryBufferForFile(DirectoryBuffer
, DirectoryLength
, PathPart
, &IsoFileInfo
))
214 MmHeapFree(DirectoryBuffer
);
218 MmHeapFree(DirectoryBuffer
);
221 // If we have another sub-directory to go then
222 // grab the start sector and file size
224 if ((i
+1) < NumberOfPathParts
)
226 DirectorySector
= IsoFileInfo
.FileStart
;
227 DirectoryLength
= IsoFileInfo
.FileSize
;
232 RtlCopyMemory(IsoFileInfoPointer
, &IsoFileInfo
, sizeof(ISO_FILE_INFO
));
237 LONG
IsoClose(ULONG FileId
)
239 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
241 MmHeapFree(FileHandle
);
246 LONG
IsoGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
248 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
250 DPRINTM(DPRINT_FILESYSTEM
, "IsoGetFileInformation() FileSize = %d\n", FileHandle
->FileSize
);
251 DPRINTM(DPRINT_FILESYSTEM
, "IsoGetFileInformation() FilePointer = %d\n", FileHandle
->FilePointer
);
253 RtlZeroMemory(Information
, sizeof(FILEINFORMATION
));
254 Information
->EndingAddress
.LowPart
= FileHandle
->FileSize
;
255 Information
->CurrentAddress
.LowPart
= FileHandle
->FilePointer
;
260 LONG
IsoOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
262 ISO_FILE_INFO TempFileInfo
;
263 PISO_FILE_INFO FileHandle
;
267 if (OpenMode
!= OpenReadOnly
)
270 DeviceId
= FsGetDeviceId(*FileId
);
272 DPRINTM(DPRINT_FILESYSTEM
, "IsoOpen() FileName = %s\n", Path
);
274 RtlZeroMemory(&TempFileInfo
, sizeof(TempFileInfo
));
275 ret
= IsoLookupFile(Path
, DeviceId
, &TempFileInfo
);
279 FileHandle
= MmHeapAlloc(sizeof(ISO_FILE_INFO
));
283 RtlCopyMemory(FileHandle
, &TempFileInfo
, sizeof(ISO_FILE_INFO
));
285 FsSetDeviceSpecific(*FileId
, FileHandle
);
289 LONG
IsoRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
291 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
292 UCHAR SectorBuffer
[SECTORSIZE
];
293 LARGE_INTEGER Position
;
297 ULONG OffsetInSector
;
298 ULONG LengthInSector
;
299 ULONG NumberOfSectors
;
303 DPRINTM(DPRINT_FILESYSTEM
, "IsoRead() Buffer = %p, N = %lu\n", Buffer
, N
);
305 DeviceId
= FsGetDeviceId(FileId
);
309 // If they are trying to read past the
310 // end of the file then return success
313 FilePointer
= FileHandle
->FilePointer
;
314 if (FilePointer
>= FileHandle
->FileSize
)
320 // If they are trying to read more than there is to read
321 // then adjust the amount to read
323 if (FilePointer
+ N
> FileHandle
->FileSize
)
325 N
= FileHandle
->FileSize
- FilePointer
;
329 // Ok, now we have to perform at most 3 calculations
330 // I'll draw you a picture (using nifty ASCII art):
332 // CurrentFilePointer -+
334 // +----------------+
336 // +-----------+-----------+-----------+-----------+
337 // | Sector 1 | Sector 2 | Sector 3 | Sector 4 |
338 // +-----------+-----------+-----------+-----------+
340 // +---------------+--------------------+
342 // N -----------------+
344 // 1 - The first calculation (and read) will align
345 // the file pointer with the next sector
346 // boundary (if we are supposed to read that much)
347 // 2 - The next calculation (and read) will read
348 // in all the full sectors that the requested
349 // amount of data would cover (in this case
351 // 3 - The last calculation (and read) would read
352 // in the remainder of the data requested out of
358 // Only do the first read if we
359 // aren't aligned on a cluster boundary
361 if (FilePointer
% SECTORSIZE
)
364 // Do the math for our first read
366 SectorNumber
= FileHandle
->FileStart
+ (FilePointer
/ SECTORSIZE
);
367 OffsetInSector
= FilePointer
% SECTORSIZE
;
368 LengthInSector
= (N
> (SECTORSIZE
- OffsetInSector
)) ? (SECTORSIZE
- OffsetInSector
) : N
;
371 // Now do the read and update Count, N, FilePointer, & Buffer
373 Position
.HighPart
= 0;
374 Position
.LowPart
= SectorNumber
* SECTORSIZE
;
375 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
380 ret
= ArcRead(DeviceId
, SectorBuffer
, SECTORSIZE
, &BytesRead
);
381 if (ret
!= ESUCCESS
|| BytesRead
!= SECTORSIZE
)
385 RtlCopyMemory(Buffer
, SectorBuffer
+ OffsetInSector
, LengthInSector
);
386 *Count
+= LengthInSector
;
388 FilePointer
+= LengthInSector
;
389 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ LengthInSector
);
393 // Do the math for our second read (if any data left)
398 // Determine how many full clusters we need to read
400 NumberOfSectors
= (N
/ SECTORSIZE
);
402 SectorNumber
= FileHandle
->FileStart
+ (FilePointer
/ SECTORSIZE
);
405 // Now do the read and update Count, N, FilePointer, & Buffer
407 Position
.HighPart
= 0;
408 Position
.LowPart
= SectorNumber
* SECTORSIZE
;
409 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
414 ret
= ArcRead(DeviceId
, Buffer
, NumberOfSectors
* SECTORSIZE
, &BytesRead
);
415 if (ret
!= ESUCCESS
|| BytesRead
!= NumberOfSectors
* SECTORSIZE
)
420 *Count
+= NumberOfSectors
* SECTORSIZE
;
421 N
-= NumberOfSectors
* SECTORSIZE
;
422 FilePointer
+= NumberOfSectors
* SECTORSIZE
;
423 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ NumberOfSectors
* SECTORSIZE
);
427 // Do the math for our third read (if any data left)
431 SectorNumber
= FileHandle
->FileStart
+ (FilePointer
/ SECTORSIZE
);
434 // Now do the read and update Count, N, FilePointer, & Buffer
436 Position
.HighPart
= 0;
437 Position
.LowPart
= SectorNumber
* SECTORSIZE
;
438 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
443 ret
= ArcRead(DeviceId
, SectorBuffer
, SECTORSIZE
, &BytesRead
);
444 if (ret
!= ESUCCESS
|| BytesRead
!= SECTORSIZE
)
448 RtlCopyMemory(Buffer
, SectorBuffer
, N
);
453 DPRINTM(DPRINT_FILESYSTEM
, "IsoRead() done\n");
458 LONG
IsoSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
460 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
462 DPRINTM(DPRINT_FILESYSTEM
, "IsoSeek() NewFilePointer = %lu\n", Position
->LowPart
);
464 if (SeekMode
!= SeekAbsolute
)
466 if (Position
->HighPart
!= 0)
468 if (Position
->LowPart
>= FileHandle
->FileSize
)
471 FileHandle
->FilePointer
= Position
->LowPart
;
475 const DEVVTBL Iso9660FuncTable
=
478 IsoGetFileInformation
,
485 const DEVVTBL
* IsoMount(ULONG DeviceId
)
487 UCHAR Buffer
[SECTORSIZE
];
488 PPVD Pvd
= (PPVD
)Buffer
;
489 LARGE_INTEGER Position
;
494 // Read The Primary Volume Descriptor
496 Position
.HighPart
= 0;
497 Position
.LowPart
= 16 * SECTORSIZE
;
498 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
501 ret
= ArcRead(DeviceId
, Pvd
, SECTORSIZE
, &Count
);
502 if (ret
!= ESUCCESS
|| Count
< sizeof(PVD
))
506 // Check if PVD is valid. If yes, return ISO9660 function table
508 if (Pvd
->VdType
== 1 && RtlEqualMemory(Pvd
->StandardId
, "CD001", 5))
509 return &Iso9660FuncTable
;