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 #define SECTORSIZE 2048
27 static BOOLEAN
IsoSearchDirectoryBufferForFile(PVOID DirectoryBuffer
, ULONG DirectoryLength
, PCHAR FileName
, PISO_FILE_INFO IsoFileInfoPointer
)
34 DPRINTM(DPRINT_FILESYSTEM
, "IsoSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x DirectoryLength = %d FileName = %s\n", DirectoryBuffer
, DirectoryLength
, FileName
);
36 RtlZeroMemory(Name
, 32 * sizeof(UCHAR
));
39 Record
= (PDIR_RECORD
)DirectoryBuffer
;
42 Offset
= Offset
+ Record
->RecordLength
;
43 Record
= (PDIR_RECORD
)((ULONG_PTR
)DirectoryBuffer
+ Offset
);
45 if (Record
->RecordLength
== 0)
47 Offset
= ROUND_UP(Offset
, SECTORSIZE
);
48 Record
= (PDIR_RECORD
)((ULONG_PTR
)DirectoryBuffer
+ Offset
);
51 if (Offset
>= DirectoryLength
)
54 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
56 DPRINTM(DPRINT_FILESYSTEM
, "Name '.'\n");
58 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
60 DPRINTM(DPRINT_FILESYSTEM
, "Name '..'\n");
64 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
65 Name
[i
] = Record
->FileId
[i
];
67 DPRINTM(DPRINT_FILESYSTEM
, "Name '%s'\n", Name
);
69 if (strlen(FileName
) == strlen(Name
) && _stricmp(FileName
, Name
) == 0)
71 IsoFileInfoPointer
->FileStart
= Record
->ExtentLocationL
;
72 IsoFileInfoPointer
->FileSize
= Record
->DataLengthL
;
73 IsoFileInfoPointer
->FilePointer
= 0;
74 IsoFileInfoPointer
->Directory
= (Record
->FileFlags
& 0x02)?TRUE
:FALSE
;
81 RtlZeroMemory(Name
, 32 * sizeof(UCHAR
));
89 * IsoBufferDirectory()
90 * This function allocates a buffer, reads the specified directory
91 * and returns a pointer to that buffer into pDirectoryBuffer. The
92 * function returns an ARC error code. The directory is specified
93 * by its starting sector and length.
95 static LONG
IsoBufferDirectory(ULONG DeviceId
, ULONG DirectoryStartSector
, ULONG DirectoryLength
,
96 PVOID
* pDirectoryBuffer
)
98 PVOID DirectoryBuffer
;
100 LARGE_INTEGER Position
;
104 DPRINTM(DPRINT_FILESYSTEM
, "IsoBufferDirectory() DirectoryStartSector = %d DirectoryLength = %d\n", DirectoryStartSector
, DirectoryLength
);
106 SectorCount
= ROUND_UP(DirectoryLength
, SECTORSIZE
) / SECTORSIZE
;
107 DPRINTM(DPRINT_FILESYSTEM
, "Trying to read (DirectoryCount) %d sectors.\n", SectorCount
);
110 // Attempt to allocate memory for directory buffer
112 DPRINTM(DPRINT_FILESYSTEM
, "Trying to allocate (DirectoryLength) %d bytes.\n", DirectoryLength
);
113 DirectoryBuffer
= MmHeapAlloc(DirectoryLength
);
114 if (!DirectoryBuffer
)
118 // Now read directory contents into DirectoryBuffer
120 Position
.HighPart
= 0;
121 Position
.LowPart
= DirectoryStartSector
* SECTORSIZE
;
122 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
125 MmHeapFree(DirectoryBuffer
);
128 ret
= ArcRead(DeviceId
, DirectoryBuffer
, SectorCount
* SECTORSIZE
, &Count
);
129 if (ret
!= ESUCCESS
|| Count
!= SectorCount
* SECTORSIZE
)
131 MmHeapFree(DirectoryBuffer
);
135 *pDirectoryBuffer
= DirectoryBuffer
;
142 * This function searches the file system for the
143 * specified filename and fills in an ISO_FILE_INFO structure
144 * with info describing the file, etc. returns ARC error code
146 static LONG
IsoLookupFile(PCSTR FileName
, ULONG DeviceId
, PISO_FILE_INFO IsoFileInfoPointer
)
148 UCHAR Buffer
[SECTORSIZE
];
149 PPVD Pvd
= (PPVD
)Buffer
;
151 ULONG NumberOfPathParts
;
153 PVOID DirectoryBuffer
;
154 ULONG DirectorySector
;
155 ULONG DirectoryLength
;
156 ISO_FILE_INFO IsoFileInfo
;
157 LARGE_INTEGER Position
;
161 DPRINTM(DPRINT_FILESYSTEM
, "IsoLookupFile() FileName = %s\n", FileName
);
163 RtlZeroMemory(IsoFileInfoPointer
, sizeof(ISO_FILE_INFO
));
166 // Read The Primary Volume Descriptor
168 Position
.HighPart
= 0;
169 Position
.LowPart
= 16 * SECTORSIZE
;
170 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
173 ret
= ArcRead(DeviceId
, Pvd
, SECTORSIZE
, &Count
);
174 if (ret
!= ESUCCESS
|| Count
< sizeof(PVD
))
177 DirectorySector
= Pvd
->RootDirRecord
.ExtentLocationL
;
178 DirectoryLength
= Pvd
->RootDirRecord
.DataLengthL
;
181 // Figure out how many sub-directories we are nested in
183 NumberOfPathParts
= FsGetNumPathParts(FileName
);
186 // Loop once for each part
188 for (i
=0; i
<NumberOfPathParts
; i
++)
191 // Get first path part
193 FsGetFirstNameFromPath(PathPart
, FileName
);
196 // Advance to the next part of the path
198 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
204 // Buffer the directory contents
206 ret
= IsoBufferDirectory(DeviceId
, DirectorySector
, DirectoryLength
, &DirectoryBuffer
);
211 // Search for file name in directory
213 if (!IsoSearchDirectoryBufferForFile(DirectoryBuffer
, DirectoryLength
, PathPart
, &IsoFileInfo
))
215 MmHeapFree(DirectoryBuffer
);
219 MmHeapFree(DirectoryBuffer
);
222 // If we have another sub-directory to go then
223 // grab the start sector and file size
225 if ((i
+1) < NumberOfPathParts
)
227 DirectorySector
= IsoFileInfo
.FileStart
;
228 DirectoryLength
= IsoFileInfo
.FileSize
;
233 RtlCopyMemory(IsoFileInfoPointer
, &IsoFileInfo
, sizeof(ISO_FILE_INFO
));
238 LONG
IsoClose(ULONG FileId
)
240 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
242 MmHeapFree(FileHandle
);
247 LONG
IsoGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
249 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
251 DPRINTM(DPRINT_FILESYSTEM
, "IsoGetFileInformation() FileSize = %d\n", FileHandle
->FileSize
);
252 DPRINTM(DPRINT_FILESYSTEM
, "IsoGetFileInformation() FilePointer = %d\n", FileHandle
->FilePointer
);
254 RtlZeroMemory(Information
, sizeof(FILEINFORMATION
));
255 Information
->EndingAddress
.LowPart
= FileHandle
->FileSize
;
256 Information
->CurrentAddress
.LowPart
= FileHandle
->FilePointer
;
261 LONG
IsoOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
263 ISO_FILE_INFO TempFileInfo
;
264 PISO_FILE_INFO FileHandle
;
268 if (OpenMode
!= OpenReadOnly
)
271 DeviceId
= FsGetDeviceId(*FileId
);
273 DPRINTM(DPRINT_FILESYSTEM
, "IsoOpen() FileName = %s\n", Path
);
275 RtlZeroMemory(&TempFileInfo
, sizeof(TempFileInfo
));
276 ret
= IsoLookupFile(Path
, DeviceId
, &TempFileInfo
);
280 FileHandle
= MmHeapAlloc(sizeof(ISO_FILE_INFO
));
284 RtlCopyMemory(FileHandle
, &TempFileInfo
, sizeof(ISO_FILE_INFO
));
286 FsSetDeviceSpecific(*FileId
, FileHandle
);
290 LONG
IsoRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
292 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
293 UCHAR SectorBuffer
[SECTORSIZE
];
294 LARGE_INTEGER Position
;
298 ULONG OffsetInSector
;
299 ULONG LengthInSector
;
300 ULONG NumberOfSectors
;
304 DPRINTM(DPRINT_FILESYSTEM
, "IsoRead() Buffer = %p, N = %lu\n", Buffer
, N
);
306 DeviceId
= FsGetDeviceId(FileId
);
310 // If they are trying to read past the
311 // end of the file then return success
314 FilePointer
= FileHandle
->FilePointer
;
315 if (FilePointer
>= FileHandle
->FileSize
)
321 // If they are trying to read more than there is to read
322 // then adjust the amount to read
324 if (FilePointer
+ N
> FileHandle
->FileSize
)
326 N
= FileHandle
->FileSize
- FilePointer
;
330 // Ok, now we have to perform at most 3 calculations
331 // I'll draw you a picture (using nifty ASCII art):
333 // CurrentFilePointer -+
335 // +----------------+
337 // +-----------+-----------+-----------+-----------+
338 // | Sector 1 | Sector 2 | Sector 3 | Sector 4 |
339 // +-----------+-----------+-----------+-----------+
341 // +---------------+--------------------+
343 // N -----------------+
345 // 1 - The first calculation (and read) will align
346 // the file pointer with the next sector
347 // boundary (if we are supposed to read that much)
348 // 2 - The next calculation (and read) will read
349 // in all the full sectors that the requested
350 // amount of data would cover (in this case
352 // 3 - The last calculation (and read) would read
353 // in the remainder of the data requested out of
359 // Only do the first read if we
360 // aren't aligned on a cluster boundary
362 if (FilePointer
% SECTORSIZE
)
365 // Do the math for our first read
367 SectorNumber
= FileHandle
->FileStart
+ (FilePointer
/ SECTORSIZE
);
368 OffsetInSector
= FilePointer
% SECTORSIZE
;
369 LengthInSector
= (N
> (SECTORSIZE
- OffsetInSector
)) ? (SECTORSIZE
- OffsetInSector
) : N
;
372 // Now do the read and update Count, N, FilePointer, & Buffer
374 Position
.HighPart
= 0;
375 Position
.LowPart
= SectorNumber
* SECTORSIZE
;
376 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
381 ret
= ArcRead(DeviceId
, SectorBuffer
, SECTORSIZE
, &BytesRead
);
382 if (ret
!= ESUCCESS
|| BytesRead
!= SECTORSIZE
)
386 RtlCopyMemory(Buffer
, SectorBuffer
+ OffsetInSector
, LengthInSector
);
387 *Count
+= LengthInSector
;
389 FilePointer
+= LengthInSector
;
390 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ LengthInSector
);
394 // Do the math for our second read (if any data left)
399 // Determine how many full clusters we need to read
401 NumberOfSectors
= (N
/ SECTORSIZE
);
403 SectorNumber
= FileHandle
->FileStart
+ (FilePointer
/ SECTORSIZE
);
406 // Now do the read and update Count, N, FilePointer, & Buffer
408 Position
.HighPart
= 0;
409 Position
.LowPart
= SectorNumber
* SECTORSIZE
;
410 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
415 ret
= ArcRead(DeviceId
, Buffer
, NumberOfSectors
* SECTORSIZE
, &BytesRead
);
416 if (ret
!= ESUCCESS
|| BytesRead
!= NumberOfSectors
* SECTORSIZE
)
421 *Count
+= NumberOfSectors
* SECTORSIZE
;
422 N
-= NumberOfSectors
* SECTORSIZE
;
423 FilePointer
+= NumberOfSectors
* SECTORSIZE
;
424 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ NumberOfSectors
* SECTORSIZE
);
428 // Do the math for our third read (if any data left)
432 SectorNumber
= FileHandle
->FileStart
+ (FilePointer
/ SECTORSIZE
);
435 // Now do the read and update Count, N, FilePointer, & Buffer
437 Position
.HighPart
= 0;
438 Position
.LowPart
= SectorNumber
* SECTORSIZE
;
439 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
444 ret
= ArcRead(DeviceId
, SectorBuffer
, SECTORSIZE
, &BytesRead
);
445 if (ret
!= ESUCCESS
|| BytesRead
!= SECTORSIZE
)
449 RtlCopyMemory(Buffer
, SectorBuffer
, N
);
454 DPRINTM(DPRINT_FILESYSTEM
, "IsoRead() done\n");
459 LONG
IsoSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
461 PISO_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
463 DPRINTM(DPRINT_FILESYSTEM
, "IsoSeek() NewFilePointer = %lu\n", Position
->LowPart
);
465 if (SeekMode
!= SeekAbsolute
)
467 if (Position
->HighPart
!= 0)
469 if (Position
->LowPart
>= FileHandle
->FileSize
)
472 FileHandle
->FilePointer
= Position
->LowPart
;
476 const DEVVTBL Iso9660FuncTable
=
479 IsoGetFileInformation
,
486 const DEVVTBL
* IsoMount(ULONG DeviceId
)
488 UCHAR Buffer
[SECTORSIZE
];
489 PPVD Pvd
= (PPVD
)Buffer
;
490 LARGE_INTEGER Position
;
495 // Read The Primary Volume Descriptor
497 Position
.HighPart
= 0;
498 Position
.LowPart
= 16 * SECTORSIZE
;
499 ret
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
502 ret
= ArcRead(DeviceId
, Pvd
, SECTORSIZE
, &Count
);
503 if (ret
!= ESUCCESS
|| Count
< sizeof(PVD
))
507 // Check if PVD is valid. If yes, return ISO9660 function table
509 if (Pvd
->VdType
== 1 && RtlEqualMemory(Pvd
->StandardId
, "CD001", 5))
510 return &Iso9660FuncTable
;