1/*
2 * Copyright 2012, J��r��me Duval, korli@users.berlios.de.
3 * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "Recognition.h"
9
10#include "UdfString.h"
11#include "MemoryChunk.h"
12#include "Utils.h"
13
14#include <string.h>
15
16
17//------------------------------------------------------------------------------
18// forward declarations
19//------------------------------------------------------------------------------
20
21static status_t
22walk_volume_recognition_sequence(int device, off_t offset, uint32 blockSize,
23	uint32 blockShift);
24
25static status_t
26walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length,
27	uint32 blockSize, uint32 blockShift,
28	primary_volume_descriptor &primaryVolumeDescriptor,
29	logical_volume_descriptor &logicalVolumeDescriptor,
30	partition_descriptor partitionDescriptors[],
31	uint8 &partitionDescriptorCount);
32
33static status_t
34walk_volume_descriptor_sequence(extent_address descriptorSequence, int device,
35	uint32 blockSize, uint32 blockShift,
36	primary_volume_descriptor &primaryVolumeDescriptor,
37	logical_volume_descriptor &logicalVolumeDescriptor,
38	partition_descriptor partitionDescriptors[],
39	uint8 &partitionDescriptorCount);
40
41static status_t
42walk_integrity_sequence(int device, uint32 blockSize, uint32 blockShift,
43	extent_address descriptorSequence, uint32 sequenceNumber = 0);
44
45//------------------------------------------------------------------------------
46// externally visible functions
47//------------------------------------------------------------------------------
48
49
50status_t
51udf_recognize(int device, off_t offset, off_t length, uint32 blockSize,
52	uint32 &blockShift, primary_volume_descriptor &primaryVolumeDescriptor,
53	logical_volume_descriptor &logicalVolumeDescriptor,
54	partition_descriptor partitionDescriptors[],
55	uint8 &partitionDescriptorCount)
56{
57	TRACE(("udf_recognize: device: = %d, offset = %" B_PRIdOFF ", length = %"
58		B_PRIdOFF ", blockSize = %" B_PRIu32 "\n", device, offset, length,
59		blockSize));
60	// Check the block size
61	status_t status = get_block_shift(blockSize, blockShift);
62	if (status != B_OK) {
63		TRACE_ERROR(("\nudf_recognize: Block size must be a positive power of "
64			"two! (blockSize = %" B_PRIu32 ")\n", blockSize));
65		return status;
66	}
67	TRACE(("blockShift: %" B_PRIu32 "\n", blockShift));
68
69	// Check for a valid volume recognition sequence
70	status = walk_volume_recognition_sequence(device, offset, blockSize,
71		blockShift);
72	if (status != B_OK) {
73		TRACE(("udf_recognize: Invalid sequence. status = %" B_PRId32
74			"\n", status));
75		return status;
76	}
77	// Now hunt down a volume descriptor sequence from one of
78	// the anchor volume pointers (if there are any).
79	status = walk_anchor_volume_descriptor_sequences(device, offset, length,
80		blockSize, blockShift, primaryVolumeDescriptor,
81		logicalVolumeDescriptor, partitionDescriptors,
82		partitionDescriptorCount);
83	if (status != B_OK) {
84		TRACE_ERROR(("udf_recognize: cannot find volume descriptor. status = %"
85			B_PRId32 "\n", status));
86		return status;
87	}
88	// Now walk the integrity sequence and make sure the last integrity
89	// descriptor is a closed descriptor
90	status = walk_integrity_sequence(device, blockSize, blockShift,
91		logicalVolumeDescriptor.integrity_sequence_extent());
92	if (status != B_OK) {
93		TRACE_ERROR(("udf_recognize: last integrity descriptor not closed. "
94			"status = %" B_PRId32 "\n", status));
95		return status;
96	}
97
98	return B_OK;
99}
100
101//------------------------------------------------------------------------------
102// local functions
103//------------------------------------------------------------------------------
104
105static
106status_t
107walk_volume_recognition_sequence(int device, off_t offset, uint32 blockSize,
108	uint32 blockShift)
109{
110	TRACE(("walk_volume_recognition_sequence: device = %d, offset = %"
111		B_PRIdOFF ", blockSize = %" B_PRIu32 ", blockShift = %" B_PRIu32 "\n",
112		device, offset, blockSize, blockShift));
113
114	// vrs starts at block 16. Each volume structure descriptor (vsd)
115	// should be one block long. We're expecting to find 0 or more iso9660
116	// vsd's followed by some ECMA-167 vsd's.
117	MemoryChunk chunk(blockSize);
118	if (chunk.InitCheck() != B_OK) {
119		TRACE_ERROR(("walk_volume_recognition_sequence: Failed to construct "
120			"MemoryChunk\n"));
121		return chunk.InitCheck();
122	}
123
124	bool foundExtended = false;
125	bool foundECMA167 = false;
126	bool foundECMA168 = false;
127	for (uint32 block = 16; true; block++) {
128		off_t address = (offset + block) << blockShift;
129		TRACE(("walk_volume_recognition_sequence: block = %" B_PRIu32 ", "
130			"address = %" B_PRIdOFF ", ", block, address));
131		ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize);
132		if (bytesRead == (ssize_t)blockSize) {
133			volume_structure_descriptor_header* descriptor
134				= (volume_structure_descriptor_header *)(chunk.Data());
135			if (descriptor->id_matches(kVSDID_ISO)) {
136				TRACE(("found ISO9660 descriptor\n"));
137			} else if (descriptor->id_matches(kVSDID_BEA)) {
138				TRACE(("found BEA descriptor\n"));
139				foundExtended = true;
140			} else if (descriptor->id_matches(kVSDID_TEA)) {
141				TRACE(("found TEA descriptor\n"));
142				foundExtended = true;
143			} else if (descriptor->id_matches(kVSDID_ECMA167_2)) {
144				TRACE(("found ECMA-167 rev 2 descriptor\n"));
145				foundECMA167 = true;
146			} else if (descriptor->id_matches(kVSDID_ECMA167_3)) {
147				TRACE(("found ECMA-167 rev 3 descriptor\n"));
148				foundECMA167 = true;
149			} else if (descriptor->id_matches(kVSDID_BOOT)) {
150				TRACE(("found boot descriptor\n"));
151			} else if (descriptor->id_matches(kVSDID_ECMA168)) {
152				TRACE(("found ECMA-168 descriptor\n"));
153				foundECMA168 = true;
154			} else {
155				TRACE(("found invalid descriptor, id = `%.5s'\n", descriptor->id));
156				break;
157			}
158		} else {
159			TRACE(("read_pos(pos:%" B_PRIdOFF ", len:%" B_PRIu32 ") "
160				"failed with: 0x%lx\n", address, blockSize, bytesRead));
161			break;
162		}
163	}
164
165	// If we find an ECMA-167 descriptor, OR if we find a beginning
166	// or terminating extended area descriptor with NO ECMA-168
167	// descriptors, we return B_OK to signal that we should go
168	// looking for valid anchors.
169	return foundECMA167 || (foundExtended && !foundECMA168) ? B_OK : B_ERROR;
170}
171
172
173static status_t
174walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length,
175	uint32 blockSize, uint32 blockShift,
176	primary_volume_descriptor &primaryVolumeDescriptor,
177	logical_volume_descriptor &logicalVolumeDescriptor,
178	partition_descriptor partitionDescriptors[],
179	uint8 &partitionDescriptorCount)
180{
181	DEBUG_INIT(NULL);
182	const uint8 avds_location_count = 4;
183	const off_t avds_locations[avds_location_count]
184		= { 256, length-1-256, length-1, 512, };
185	bool found_vds = false;
186	for (int32 i = 0; i < avds_location_count; i++) {
187		off_t block = avds_locations[i];
188		off_t address = (offset + block) << blockShift;
189		MemoryChunk chunk(blockSize);
190		anchor_volume_descriptor *anchor = NULL;
191
192		status_t anchorErr = chunk.InitCheck();
193		if (!anchorErr) {
194			ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize);
195			anchorErr = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR;
196			if (anchorErr) {
197				PRINT(("block %" B_PRIdOFF ": read_pos(pos:%" B_PRIdOFF ", "
198						"len:%" PRIu32 ") failed with error 0x%lx\n",
199					block, address, blockSize, bytesRead));
200			}
201		}
202		if (!anchorErr) {
203			anchor = reinterpret_cast<anchor_volume_descriptor*>(chunk.Data());
204			anchorErr = anchor->tag().init_check(block + offset);
205			if (anchorErr) {
206				PRINT(("block %" B_PRIdOFF ": invalid anchor\n", block));
207			} else {
208				PRINT(("block %" B_PRIdOFF ": valid anchor\n", block));
209			}
210		}
211		if (!anchorErr) {
212			PRINT(("block %" B_PRIdOFF ": anchor:\n", block));
213			PDUMP(anchor);
214			// Found an avds, so try the main sequence first, then
215			// the reserve sequence if the main one fails.
216			anchorErr = walk_volume_descriptor_sequence(anchor->main_vds(),
217				device, blockSize, blockShift, primaryVolumeDescriptor,
218				logicalVolumeDescriptor, partitionDescriptors,
219				partitionDescriptorCount);
220
221			if (anchorErr)
222				anchorErr = walk_volume_descriptor_sequence(anchor->reserve_vds(),
223					device,	blockSize, blockShift, primaryVolumeDescriptor,
224					logicalVolumeDescriptor, partitionDescriptors,
225					partitionDescriptorCount);
226		}
227		if (!anchorErr) {
228			PRINT(("block %" B_PRIdOFF ": found valid vds\n",
229				avds_locations[i]));
230			found_vds = true;
231			break;
232		} else {
233			// Both failed, so loop around and try another avds
234			PRINT(("block %" B_PRIdOFF ": vds search failed\n",
235				avds_locations[i]));
236		}
237	}
238	status_t error = found_vds ? B_OK : B_ERROR;
239	RETURN(error);
240}
241
242static status_t
243walk_tagid_partition_descriptor(descriptor_tag  *tag, off_t block,
244	uint8& uniquePartitions, partition_descriptor* partitionDescriptors)
245{
246	DEBUG_INIT(NULL);
247
248	status_t error = B_OK;
249	partition_descriptor *partition =
250		reinterpret_cast<partition_descriptor*>(tag);
251	PDUMP(partition);
252	if (partition->tag().init_check(block) == B_OK) {
253		// Check for a previously discovered partition descriptor with
254		// the same number as this partition. If found, keep the one with
255		// the higher vds number.
256		bool foundDuplicate = false;
257		int num;
258		for (num = 0; num < uniquePartitions; num++) {
259			if (partitionDescriptors[num].partition_number()
260				== partition->partition_number()) {
261				foundDuplicate = true;
262				if (partitionDescriptors[num].vds_number()
263					< partition->vds_number()) {
264					partitionDescriptors[num] = *partition;
265					PRINT(("Replacing previous partition #%d "
266						"(vds_number: %" B_PRIu32 ") with new partition #%d "
267						"(vds_number: %" B_PRIu32 ")\n",
268						partitionDescriptors[num].partition_number(),
269						partitionDescriptors[num].vds_number(),
270						partition->partition_number(),
271						partition->vds_number()));
272				}
273				break;
274			}
275		}
276		// If we didn't find a duplicate, see if we have any open descriptor
277		// spaces left.
278		if (!foundDuplicate) {
279			if (num < kMaxPartitionDescriptors) {
280			// At least one more partition descriptor allowed
281				partitionDescriptors[num] = *partition;
282				uniquePartitions++;
283				PRINT(("Adding partition #%d (vds_number: %" B_PRIu32 ")\n",
284					partition->partition_number(),
285					partition->vds_number()));
286			} else {
287				// We've found more than kMaxPartitionDescriptor uniquely-
288				// numbered partitions. So, search through the partitions
289				// we already have again, this time just looking for a
290				// partition with a lower vds number. If we find one,
291				// replace it with this one. If we don't, scream bloody
292				// murder.
293				bool foundReplacement = false;
294				for (int j = 0; j < uniquePartitions; j++) {
295					if (partitionDescriptors[j].vds_number()
296						< partition->vds_number()) {
297						foundReplacement = true;
298						partitionDescriptors[j] = *partition;
299						PRINT(("Replacing partition #%d "
300							"(vds_number: %" B_PRIu32 ") "
301							"with partition #%d "
302							"(vds_number: %" B_PRIu32 ")\n",
303							partitionDescriptors[j].partition_number(),
304							partitionDescriptors[j].vds_number(),
305							partition->partition_number(),
306							partition->vds_number()));
307							break;
308					}
309				}
310				if (!foundReplacement) {
311					PRINT(("Found more than kMaxPartitionDescriptors == %d "
312					"unique partition descriptors!\n",
313					kMaxPartitionDescriptors));
314						error = B_BAD_VALUE;
315				}
316			}
317		}
318	}
319
320	RETURN(error);
321}
322
323static
324status_t
325walk_volume_descriptor_sequence(extent_address descriptorSequence,
326	int device, uint32 blockSize, uint32 blockShift,
327	primary_volume_descriptor &primaryVolumeDescriptor,
328	logical_volume_descriptor &logicalVolumeDescriptor,
329	partition_descriptor partitionDescriptors[],
330	uint8 &partitionDescriptorCount)
331{
332	DEBUG_INIT_ETC(NULL, ("descriptorSequence.loc:%" PRIu32 ", "
333			"descriptorSequence.len:%" PRIu32,
334		descriptorSequence.location(), descriptorSequence.length()));
335	uint32 count = descriptorSequence.length() >> blockShift;
336
337	bool foundLogicalVolumeDescriptor = false;
338	bool foundUnallocatedSpaceDescriptor = false;
339	bool foundUdfImplementationUseDescriptor = false;
340	uint8 uniquePartitions = 0;
341	status_t error = B_OK;
342
343	for (uint32 i = 0; i < count; i++) {
344		off_t block = descriptorSequence.location()+i;
345		off_t address = block << blockShift;
346		MemoryChunk chunk(blockSize);
347		descriptor_tag  *tag = NULL;
348
349		PRINT(("descriptor #%" PRIu32 " (block %" B_PRIdOFF "):\n", i, block));
350
351		status_t loopError = chunk.InitCheck();
352		if (!loopError) {
353			ssize_t bytesRead = read_pos(device, address, chunk.Data(),
354				blockSize);
355			loopError = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR;
356			if (loopError) {
357				PRINT(("block %" B_PRIdOFF ": read_pos(pos:%" B_PRIdOFF ", "
358						"len:%" B_PRIu32 ") failed with error 0x%lx\n",
359					block, address, blockSize, bytesRead));
360			}
361		}
362		if (!loopError) {
363			tag = reinterpret_cast<descriptor_tag *>(chunk.Data());
364			loopError = tag->init_check(block);
365		}
366		if (!loopError) {
367			// Now decide what type of descriptor we have
368			switch (tag->id()) {
369				case TAGID_UNDEFINED:
370					break;
371
372				case TAGID_PRIMARY_VOLUME_DESCRIPTOR:
373				{
374					primary_volume_descriptor *primary =
375						reinterpret_cast<primary_volume_descriptor*>(tag);
376					PDUMP(primary);
377					primaryVolumeDescriptor = *primary;
378					break;
379				}
380
381				case TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER:
382					break;
383
384				case TAGID_VOLUME_DESCRIPTOR_POINTER:
385					break;
386
387				case TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR:
388				{
389					implementation_use_descriptor *impUse =
390						reinterpret_cast<implementation_use_descriptor*>(tag);
391					PDUMP(impUse);
392					// Check for a matching implementation id string
393					//  (note that the revision version is not checked)
394					if (impUse->tag().init_check(block) == B_OK
395						&& impUse->implementation_id().matches(
396						kLogicalVolumeInfoId201)) {
397								foundUdfImplementationUseDescriptor = true;
398					}
399					break;
400				}
401
402				case TAGID_PARTITION_DESCRIPTOR:
403				{
404					error = walk_tagid_partition_descriptor(
405						tag, block, uniquePartitions, partitionDescriptors);
406					break;
407				}
408
409				case TAGID_LOGICAL_VOLUME_DESCRIPTOR:
410				{
411					logical_volume_descriptor *logical =
412						reinterpret_cast<logical_volume_descriptor*>(tag);
413					PDUMP(logical);
414					if (foundLogicalVolumeDescriptor) {
415						// Keep the vd with the highest vds_number
416						if (logicalVolumeDescriptor.vds_number()
417							< logical->vds_number()) {
418								logicalVolumeDescriptor = *logical;
419						}
420					} else {
421						logicalVolumeDescriptor = *logical;
422						foundLogicalVolumeDescriptor = true;
423					}
424					break;
425				}
426
427				case TAGID_UNALLOCATED_SPACE_DESCRIPTOR:
428				{
429					unallocated_space_descriptor *unallocated =
430						reinterpret_cast<unallocated_space_descriptor*>(tag);
431					PDUMP(unallocated);
432					foundUnallocatedSpaceDescriptor = true;
433					(void)unallocated;	// kill the warning
434					break;
435				}
436
437				case TAGID_TERMINATING_DESCRIPTOR:
438				{
439					terminating_descriptor *terminating =
440						reinterpret_cast<terminating_descriptor*>(tag);
441					PDUMP(terminating);
442					(void)terminating;	// kill the warning
443					break;
444				}
445
446				case TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR:
447					// Not found in this descriptor sequence
448					break;
449
450				default:
451					break;
452
453			}
454		}
455	}
456
457	PRINT(("found %d unique partition%s\n", uniquePartitions,
458		(uniquePartitions == 1 ? "" : "s")));
459
460	if (!error && !foundUdfImplementationUseDescriptor) {
461		INFORM(("WARNING: no valid udf implementation use descriptor found\n"));
462	}
463	if (!error)
464		error = foundLogicalVolumeDescriptor
465			&& foundUnallocatedSpaceDescriptor
466			? B_OK : B_ERROR;
467	if (!error)
468		error = uniquePartitions >= 1 ? B_OK : B_ERROR;
469	if (!error)
470		partitionDescriptorCount = uniquePartitions;
471
472	RETURN(error);
473}
474
475/*! \brief Walks the integrity sequence in the extent given by \a descriptorSequence.
476
477	\return
478	- \c B_OK: Success. the sequence was terminated by a valid, closed
479		integrity descriptor.
480	- \c B_ENTRY_NOT_FOUND: The sequence was empty.
481	- (other error code): The sequence was non-empty and did not end in a valid,
482                          closed integrity descriptor.
483*/
484static status_t
485walk_integrity_sequence(int device, uint32 blockSize, uint32 blockShift,
486                        extent_address descriptorSequence, uint32 sequenceNumber)
487{
488	DEBUG_INIT_ETC(NULL,
489		("descriptorSequence.loc:%" B_PRIu32 ", "
490		"descriptorSequence.len:%" B_PRIu32 ,
491		descriptorSequence.location(), descriptorSequence.length()));
492	uint32 count = descriptorSequence.length() >> blockShift;
493
494	bool lastDescriptorWasClosed = false;
495	uint16 highestMinimumUDFReadRevision = 0x0000;
496	status_t error = count > 0 ? B_OK : B_ENTRY_NOT_FOUND;
497	for (uint32 i = 0; error == B_OK && i < count; i++) {
498		off_t block = descriptorSequence.location()+i;
499		off_t address = block << blockShift;
500		MemoryChunk chunk(blockSize);
501		descriptor_tag *tag = NULL;
502
503		PRINT(("integrity descriptor #%" B_PRIu32 ":%" B_PRIu32
504			" (block %" B_PRIdOFF "):\n",
505			sequenceNumber, i, block));
506
507		status_t loopError = chunk.InitCheck();
508		if (!loopError) {
509			ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize);
510			loopError = check_size_error(bytesRead, blockSize);
511			if (loopError) {
512				PRINT(("block %" B_PRIdOFF": read_pos(pos:%" B_PRIdOFF
513					", len:%" B_PRIu32 ") failed with error 0x%lx\n",
514					block, address, blockSize, bytesRead));
515			}
516		}
517		if (!loopError) {
518			tag = reinterpret_cast<descriptor_tag *>(chunk.Data());
519			loopError = tag->init_check(block);
520		}
521		if (!loopError) {
522			// Check the descriptor type and see if it's closed.
523			loopError = tag->id() == TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR
524				? B_OK : B_BAD_DATA;
525			if (!loopError) {
526				logical_volume_integrity_descriptor *descriptor =
527					reinterpret_cast<logical_volume_integrity_descriptor*>(chunk.Data());
528				PDUMP(descriptor);
529				lastDescriptorWasClosed = descriptor->integrity_type() == INTEGRITY_CLOSED;
530				if (lastDescriptorWasClosed) {
531					uint16 minimumRevision = descriptor->minimum_udf_read_revision();
532					if (minimumRevision > highestMinimumUDFReadRevision) {
533						highestMinimumUDFReadRevision = minimumRevision;
534					} else if (minimumRevision < highestMinimumUDFReadRevision) {
535						INFORM(("WARNING: found decreasing minimum udf read revision in integrity "
536							"sequence (last highest: 0x%04x, current: 0x%04x); using higher "
537							"revision.\n", highestMinimumUDFReadRevision, minimumRevision));
538					}
539				}
540
541				// Check a continuation extent if necessary. Note that this effectively
542				// ends our search through this extent
543				extent_address &next = descriptor->next_integrity_extent();
544				if (next.length() > 0) {
545					status_t nextError = walk_integrity_sequence(device, blockSize, blockShift,
546						next, sequenceNumber+1);
547					if (nextError && nextError != B_ENTRY_NOT_FOUND) {
548						// Continuation proved invalid
549						error = nextError;
550						break;
551					} else {
552						// Either the continuation was valid or empty; either way,
553						// we're done searching.
554						break;
555					}
556				}
557			} else {
558				PDUMP(tag);
559			}
560		}
561		// If we hit an error on the first item, consider the extent empty,
562		// otherwise just break out of the loop and assume part of the
563		// extent is unrecorded
564		if (loopError) {
565			if (i == 0)
566				error = B_ENTRY_NOT_FOUND;
567			else
568				break;
569		}
570	}
571	if (!error)
572		error = lastDescriptorWasClosed ? B_OK : B_BAD_DATA;
573	if (error == B_OK
574		&& highestMinimumUDFReadRevision > UDF_MAX_READ_REVISION) {
575		error = B_ERROR;
576		FATAL(("found udf revision 0x%x more than max 0x%x\n",
577			highestMinimumUDFReadRevision, UDF_MAX_READ_REVISION));
578	}
579	RETURN(error);
580}
581