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