Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

593 lines
16 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. udfs_rec.c
  5. Abstract:
  6. This module contains the mini-file system recognizer for UDFS.
  7. Author:
  8. Dan Lovinger (danlo) 13-Feb-1997
  9. Environment:
  10. Kernel mode, local to I/O system
  11. Revision History:
  12. --*/
  13. #include "fs_rec.h"
  14. #include "udfs_rec.h"
  15. //
  16. // The local debug trace level
  17. //
  18. #define Dbg (FSREC_DEBUG_LEVEL_UDFS)
  19. //
  20. // Tables of tokens we have to parse up from mount-time on-disk structures
  21. //
  22. PARSE_KEYVALUE VsdIdentParseTable[] = {
  23. { VSD_IDENT_BEA01, VsdIdentBEA01 },
  24. { VSD_IDENT_TEA01, VsdIdentTEA01 },
  25. { VSD_IDENT_CDROM, VsdIdentCDROM },
  26. { VSD_IDENT_CD001, VsdIdentCD001 },
  27. { VSD_IDENT_CDW01, VsdIdentCDW01 },
  28. { VSD_IDENT_CDW02, VsdIdentCDW02 },
  29. { VSD_IDENT_NSR01, VsdIdentNSR01 },
  30. { VSD_IDENT_NSR02, VsdIdentNSR02 },
  31. { VSD_IDENT_BOOT2, VsdIdentBOOT2 },
  32. { VSD_IDENT_NSR03, VsdIdentNSR03 },
  33. { NULL, VsdIdentBad }
  34. };
  35. NTSTATUS
  36. UdfsRecGetLastSessionStart(
  37. IN PDEVICE_OBJECT DeviceObject,
  38. OUT PULONG Psn
  39. );
  40. #ifdef ALLOC_PRAGMA
  41. #pragma alloc_text(PAGE,IsUdfsVolume)
  42. #pragma alloc_text(PAGE,UdfsFindInParseTable)
  43. #pragma alloc_text(PAGE,UdfsRecFsControl)
  44. #pragma alloc_text(PAGE,UdfsRecGetLastSessionStart)
  45. #endif // ALLOC_PRAGMA
  46. //
  47. // This macro copies an unaligned src longword to a dst longword,
  48. // performing an little/big endian swap.
  49. //
  50. #define SwapCopyUchar4(Dst,Src) { \
  51. *((UNALIGNED UCHAR *)(Dst)) = *((UNALIGNED UCHAR *)(Src) + 3); \
  52. *((UNALIGNED UCHAR *)(Dst) + 1) = *((UNALIGNED UCHAR *)(Src) + 2); \
  53. *((UNALIGNED UCHAR *)(Dst) + 2) = *((UNALIGNED UCHAR *)(Src) + 1); \
  54. *((UNALIGNED UCHAR *)(Dst) + 3) = *((UNALIGNED UCHAR *)(Src)); \
  55. }
  56. NTSTATUS
  57. UdfsRecGetLastSessionStart(
  58. IN PDEVICE_OBJECT DeviceObject,
  59. OUT PULONG Psn
  60. )
  61. /*++
  62. Routine Description:
  63. This function queries the underlying device for the address of the
  64. first track in the last session. Does nothing for DISK devices.
  65. Arguments:
  66. DeviceObject - Pointer to this driver's device object.
  67. Psn - receives physical sector number of first block in last session,
  68. 0 for disk devices
  69. Return Value:
  70. The function value is the final status of the operation.
  71. -*/
  72. {
  73. KEVENT Event;
  74. NTSTATUS Status;
  75. IO_STATUS_BLOCK ioStatus;
  76. CDROM_TOC_SESSION_DATA SessionData;
  77. PIRP Irp;
  78. *Psn = 0;
  79. if (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM) {
  80. return STATUS_SUCCESS;
  81. }
  82. KeInitializeEvent( &Event, SynchronizationEvent, FALSE );
  83. Irp = IoBuildDeviceIoControlRequest( IOCTL_CDROM_GET_LAST_SESSION,
  84. DeviceObject,
  85. (PVOID) NULL,
  86. 0,
  87. &SessionData,
  88. sizeof( SessionData ),
  89. FALSE,
  90. &Event,
  91. &ioStatus );
  92. if (!Irp) {
  93. return STATUS_INSUFFICIENT_RESOURCES;
  94. }
  95. //
  96. // Override verify logic - we don't care. The fact we're in the picture means
  97. // someone is trying to mount new/changed media in the first place.
  98. //
  99. SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
  100. Status = IoCallDriver( DeviceObject, Irp );
  101. if (Status == STATUS_PENDING) {
  102. (VOID) KeWaitForSingleObject( &Event,
  103. Executive,
  104. KernelMode,
  105. FALSE,
  106. (PLARGE_INTEGER) NULL );
  107. Status = ioStatus.Status;
  108. }
  109. if (!NT_SUCCESS( Status )) {
  110. return Status;
  111. }
  112. if (SessionData.FirstCompleteSession != SessionData.LastCompleteSession) {
  113. SwapCopyUchar4( Psn, &SessionData.TrackData[0].Address );
  114. }
  115. return STATUS_SUCCESS;
  116. }
  117. NTSTATUS
  118. UdfsRecFsControl(
  119. IN PDEVICE_OBJECT DeviceObject,
  120. IN PIRP Irp
  121. )
  122. /*++
  123. Routine Description:
  124. This function performs the mount and driver reload functions for this mini-
  125. file system recognizer driver.
  126. Arguments:
  127. DeviceObject - Pointer to this driver's device object.
  128. Irp - Pointer to the I/O Request Packet (IRP) representing the function to
  129. be performed.
  130. Return Value:
  131. The function value is the final status of the operation.
  132. -*/
  133. {
  134. NTSTATUS status;
  135. PIO_STACK_LOCATION irpSp;
  136. PDEVICE_EXTENSION deviceExtension;
  137. UNICODE_STRING driverName;
  138. ULONG bytesPerSector;
  139. PDEVICE_OBJECT targetDevice;
  140. PAGED_CODE();
  141. //
  142. // Begin by determining what function that is to be performed.
  143. //
  144. deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  145. irpSp = IoGetCurrentIrpStackLocation( Irp );
  146. switch ( irpSp->MinorFunction ) {
  147. case IRP_MN_MOUNT_VOLUME:
  148. //
  149. // Attempt to mount a volume: There are two different cases here:
  150. //
  151. // 1) The device is being opened for DASD access, that is, no
  152. // file system is required, thus it is OK to allow RAW to
  153. // to open it.
  154. //
  155. // 2) We need to rummage the media to see if this is a UDF volume.
  156. //
  157. status = STATUS_UNRECOGNIZED_VOLUME;
  158. targetDevice = irpSp->Parameters.MountVolume.DeviceObject;
  159. if (FsRecGetDeviceSectorSize( targetDevice,
  160. &bytesPerSector )) {
  161. if (IsUdfsVolume( targetDevice,
  162. bytesPerSector )) {
  163. status = STATUS_FS_DRIVER_REQUIRED;
  164. }
  165. }
  166. break;
  167. case IRP_MN_LOAD_FILE_SYSTEM:
  168. status = FsRecLoadFileSystem( DeviceObject,
  169. L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Udfs" );
  170. break;
  171. default:
  172. status = STATUS_INVALID_DEVICE_REQUEST;
  173. }
  174. //
  175. // Finally, complete the request and return the same status code to the
  176. // caller.
  177. //
  178. Irp->IoStatus.Status = status;
  179. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  180. return status;
  181. }
  182. BOOLEAN
  183. IsUdfsVolume (
  184. IN PDEVICE_OBJECT DeviceObject,
  185. IN ULONG SectorSize
  186. )
  187. /*++
  188. Routine Description:
  189. This routine walks the Volume Recognition Sequence to determine
  190. whether this volume contains an NSR02 (ISO 13346 Section 4) image.
  191. Note: this routine is pretty much diked out of UdfsRecognizeVolume
  192. in the real filesystem, modulo fitting it into the fs recognizer.
  193. Arguments:
  194. DeviceObject - device we are checking
  195. SectorSize - size of a physical sector on this device
  196. Return Value:
  197. Boolean TRUE if we found NSR02, FALSE otherwise.
  198. --*/
  199. {
  200. BOOLEAN FoundNSR;
  201. BOOLEAN FoundBEA;
  202. BOOLEAN Resolved;
  203. ULONG LastSessionStartPsn;
  204. PVSD_GENERIC VolumeStructureDescriptor = NULL;
  205. PVOID Buffer = NULL;
  206. ULONGLONG Offset;
  207. PAGED_CODE();
  208. DebugTrace(( +1, Dbg,
  209. "IsUdfsVolume, DevObj %08x SectorSize %08x\n",
  210. DeviceObject,
  211. SectorSize ));
  212. //
  213. // Find the start of the last session
  214. //
  215. if (!NT_SUCCESS( UdfsRecGetLastSessionStart( DeviceObject,
  216. &LastSessionStartPsn))) {
  217. return FALSE;
  218. }
  219. Retry:
  220. DebugTrace(( 0, Dbg, "IsUdfsVolume, Looking at session starting Psn == 0x%x\n", LastSessionStartPsn));
  221. Offset = (SectorSize * LastSessionStartPsn) + SectorAlignN( SectorSize, VRA_BOUNDARY_LOCATION );
  222. FoundNSR =
  223. FoundBEA =
  224. Resolved = FALSE;
  225. while (!Resolved) {
  226. //
  227. // The VRS descriptors are specified at 2kb regardless of sector size. So
  228. // we only want to read if we've processed all 2kb blocks in the last read
  229. // sector (latest gen MO media has 4kb sectors) i.e. if we have landed
  230. // on a sector aligned offset after processing the last descriptor.
  231. //
  232. if (0 == (Offset & (SectorSize - 1))) {
  233. if (!FsRecReadBlock( DeviceObject,
  234. (PLARGE_INTEGER)&Offset,
  235. sizeof(VSD_GENERIC),
  236. SectorSize,
  237. &Buffer,
  238. NULL )) {
  239. break;
  240. }
  241. VolumeStructureDescriptor = Buffer;
  242. }
  243. //
  244. // Now check the type of the descriptor. All ISO 13346 VSDs are
  245. // of Type 0, 9660 PVDs are Type 1, 9660 SVDs are Type 2, and 9660
  246. // terminating descriptors are Type 255.
  247. //
  248. if (VolumeStructureDescriptor->Type == 0) {
  249. //
  250. // In order to properly recognize the volume, we must know all of the
  251. // Structure identifiers in ISO 13346 so that we can terminate if a
  252. // badly formatted (or, shockingly, non 13346) volume is presented to us.
  253. //
  254. switch (UdfsFindInParseTable( VsdIdentParseTable,
  255. VolumeStructureDescriptor->Ident,
  256. VSD_LENGTH_IDENT )) {
  257. case VsdIdentBEA01:
  258. //
  259. // Only one BEA may exist and its version must be 1 (2/9.2.3)
  260. //
  261. DebugTrace(( 0, Dbg, "IsUdfsVolume, got a BEA01\n" ));
  262. if ((FoundBEA &&
  263. DebugTrace(( 0, Dbg,
  264. "IsUdfsVolume, ... but it is a duplicate!\n" ))) ||
  265. (VolumeStructureDescriptor->Version != 1 &&
  266. DebugTrace(( 0, Dbg,
  267. "IsUdfsVolume, ... but it has a wacky version number %02x != 1!\n",
  268. VolumeStructureDescriptor->Version )))) {
  269. Resolved = TRUE;
  270. break;
  271. }
  272. FoundBEA = TRUE;
  273. break;
  274. case VsdIdentTEA01:
  275. //
  276. // If we reach the TEA it must be the case that we don't recognize
  277. //
  278. DebugTrace(( 0, Dbg, "IsUdfsVolume, got a TEA01\n" ));
  279. Resolved = TRUE;
  280. break;
  281. case VsdIdentNSR02:
  282. case VsdIdentNSR03:
  283. //
  284. // We recognize NSR02 version 1 embedded after a BEA (3/9.1.3). For
  285. // simplicity we will not bother being a complete nitpick and check
  286. // for a bounding TEA, although we will be optimistic in the case where
  287. // we fail to match the version.
  288. //
  289. DebugTrace(( 0, Dbg, "IsUdfsVolume, got an NSR02/3\n" ));
  290. if ((FoundBEA ||
  291. !DebugTrace(( 0, Dbg, "IsUdfsVolume, ... but we haven't seen a BEA01 yet!\n" ))) &&
  292. (VolumeStructureDescriptor->Version == 1 ||
  293. !DebugTrace(( 0, Dbg, "IsUdfsVolume, ... but it has a wacky version number %02x != 1\n",
  294. VolumeStructureDescriptor->Version )))) {
  295. FoundNSR = Resolved = TRUE;
  296. break;
  297. }
  298. break;
  299. case VsdIdentCD001:
  300. case VsdIdentCDW01:
  301. case VsdIdentNSR01:
  302. case VsdIdentCDW02:
  303. case VsdIdentBOOT2:
  304. DebugTrace(( 0, Dbg, "IsUdfsVolume, got a valid but uninteresting 13346 descriptor\n" ));
  305. //
  306. // Valid but uninteresting (to us) descriptors
  307. //
  308. break;
  309. default:
  310. DebugTrace(( 0, Dbg, "IsUdfsVolume, got an invalid 13346 descriptor\n" ));
  311. //
  312. // Stumbling across something we don't know, it must be that this
  313. // is not a valid 13346 image
  314. //
  315. Resolved = TRUE;
  316. break;
  317. }
  318. } else if (!FoundBEA && (VolumeStructureDescriptor->Type < 3 ||
  319. VolumeStructureDescriptor->Type == 255)) {
  320. DebugTrace(( 0, Dbg, "IsUdfsVolume, got a 9660 descriptor\n" ));
  321. //
  322. // Only HSG (CDROM) and 9660 (CD001) are possible, and they are only legal
  323. // before the ISO 13346 BEA/TEA extent. By design, an ISO 13346 VSD precisely
  324. // overlaps a 9660 PVD/SVD in the appropriate fields.
  325. //
  326. // Note that we aren't being strict about the structure of the 9660 descriptors
  327. // since that really isn't very interesting. We care more about the 13346.
  328. //
  329. //
  330. switch (UdfsFindInParseTable( VsdIdentParseTable,
  331. VolumeStructureDescriptor->Ident,
  332. VSD_LENGTH_IDENT )) {
  333. case VsdIdentCDROM:
  334. case VsdIdentCD001:
  335. DebugTrace(( 0, Dbg, "IsUdfsVolume, ... seems we have 9660 here\n" ));
  336. //
  337. // Note to our caller that we seem to have ISO 9660 here
  338. //
  339. break;
  340. default:
  341. DebugTrace(( 0, Dbg, "IsUdfsVolume, ... but it looks wacky\n" ));
  342. //
  343. // This probably was a false alert, but in any case there is nothing
  344. // on this volume for us.
  345. //
  346. Resolved = TRUE;
  347. break;
  348. }
  349. } else {
  350. //
  351. // Something else must be recorded on this volume.
  352. //
  353. DebugTrace(( 0, Dbg, "IsUdfsVolume, got an unrecognizeable descriptor, probably not 13346/9660\n" ));
  354. break;
  355. }
  356. //
  357. // Descriptor size is 2kb. Sector size is always a power of 2. So just
  358. // increase offset by desc. size. This may fall on another 2k block within
  359. // the current sector.
  360. //
  361. Offset += sizeof(VSD_GENERIC);
  362. VolumeStructureDescriptor = (PVSD_GENERIC)(((PUCHAR)VolumeStructureDescriptor) + sizeof( VSD_GENERIC));
  363. }
  364. //
  365. // If we were looking in the last session, and failed to find anything, then
  366. // go back and try the first.
  367. //
  368. if (!FoundNSR && (0 != LastSessionStartPsn)) {
  369. LastSessionStartPsn = 0;
  370. goto Retry;
  371. }
  372. DebugTrace(( -1, Dbg, "IsUdfsVolume -> %c\n", ( FoundNSR ? 'T' : 'F' )));
  373. //
  374. // Free up our temporary buffer
  375. //
  376. if (Buffer) {
  377. ExFreePool( Buffer );
  378. }
  379. return FoundNSR;
  380. }
  381. ULONG
  382. UdfsFindInParseTable (
  383. IN PPARSE_KEYVALUE ParseTable,
  384. IN PCHAR Id,
  385. IN ULONG MaxIdLen
  386. )
  387. /*++
  388. Routine Description:
  389. This routine walks a table of string key/value information for a match of the
  390. input Id. MaxIdLen can be set to get a prefix match.
  391. Arguments:
  392. Table - This is the table being searched.
  393. Id - Key value.
  394. MaxIdLen - Maximum possible length of Id.
  395. Return Value:
  396. Value of matching entry, or the terminating (NULL) entry's value.
  397. --*/
  398. {
  399. PAGED_CODE();
  400. while (ParseTable->Key != NULL) {
  401. if (RtlEqualMemory(ParseTable->Key, Id, MaxIdLen)) {
  402. break;
  403. }
  404. ParseTable++;
  405. }
  406. return ParseTable->Value;
  407. }