xref: /haiku/src/add-ons/kernel/file_systems/udf/Recognition.cpp (revision 040a81419dda83d1014e9dc94936a4cb3f027303)
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 = %" 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_ERROR(("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 
105 static
106 status_t
107 walk_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 B_ERROR;
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_ERROR(("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 
173 static status_t
174 walk_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 %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n",
198 				       block, address, blockSize, bytesRead));
199 			}
200 		}
201 		if (!anchorErr) {
202 			anchor = reinterpret_cast<anchor_volume_descriptor*>(chunk.Data());
203 			anchorErr = anchor->tag().init_check(block + offset);
204 			if (anchorErr) {
205 				PRINT(("block %Ld: invalid anchor\n", block));
206 			} else {
207 				PRINT(("block %Ld: valid anchor\n", block));
208 			}
209 		}
210 		if (!anchorErr) {
211 			PRINT(("block %Ld: anchor:\n", block));
212 			PDUMP(anchor);
213 			// Found an avds, so try the main sequence first, then
214 			// the reserve sequence if the main one fails.
215 			anchorErr = walk_volume_descriptor_sequence(anchor->main_vds(),
216 				device, blockSize, blockShift, primaryVolumeDescriptor,
217 				logicalVolumeDescriptor, partitionDescriptors,
218 				partitionDescriptorCount);
219 
220 			if (anchorErr)
221 				anchorErr = walk_volume_descriptor_sequence(anchor->reserve_vds(),
222 					device,	blockSize, blockShift, primaryVolumeDescriptor,
223 					logicalVolumeDescriptor, partitionDescriptors,
224 					partitionDescriptorCount);
225 		}
226 		if (!anchorErr) {
227 			PRINT(("block %Ld: found valid vds\n", avds_locations[i]));
228 			found_vds = true;
229 			break;
230 		} else {
231 			// Both failed, so loop around and try another avds
232 			PRINT(("block %Ld: vds search failed\n", avds_locations[i]));
233 		}
234 	}
235 	status_t error = found_vds ? B_OK : B_ERROR;
236 	RETURN(error);
237 }
238 
239 static
240 status_t
241 walk_volume_descriptor_sequence(extent_address descriptorSequence,
242 	int device, uint32 blockSize, uint32 blockShift,
243 	primary_volume_descriptor &primaryVolumeDescriptor,
244 	logical_volume_descriptor &logicalVolumeDescriptor,
245 	partition_descriptor partitionDescriptors[],
246 	uint8 &partitionDescriptorCount)
247 {
248 	DEBUG_INIT_ETC(NULL, ("descriptorSequence.loc:%ld, descriptorSequence.len:%ld",
249 	           descriptorSequence.location(), descriptorSequence.length()));
250 	uint32 count = descriptorSequence.length() >> blockShift;
251 
252 	bool foundLogicalVolumeDescriptor = false;
253 	bool foundUnallocatedSpaceDescriptor = false;
254 	bool foundUdfImplementationUseDescriptor = false;
255 	uint8 uniquePartitions = 0;
256 	status_t error = B_OK;
257 
258 	for (uint32 i = 0; i < count; i++)
259 	{
260 		off_t block = descriptorSequence.location()+i;
261 		off_t address = block << blockShift;
262 		MemoryChunk chunk(blockSize);
263 		descriptor_tag  *tag = NULL;
264 
265 		PRINT(("descriptor #%ld (block %Ld):\n", i, block));
266 
267 		status_t loopError = chunk.InitCheck();
268 		if (!loopError) {
269 			ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize);
270 			loopError = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR;
271 			if (loopError) {
272 				PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n",
273 				       block, address, blockSize, bytesRead));
274 			}
275 		}
276 		if (!loopError) {
277 			tag = reinterpret_cast<descriptor_tag *>(chunk.Data());
278 			loopError = tag->init_check(block);
279 		}
280 		if (!loopError) {
281 			// Now decide what type of descriptor we have
282 			switch (tag->id()) {
283 				case TAGID_UNDEFINED:
284 					break;
285 
286 				case TAGID_PRIMARY_VOLUME_DESCRIPTOR:
287 				{
288 					primary_volume_descriptor *primary = reinterpret_cast<primary_volume_descriptor*>(tag);
289 					PDUMP(primary);
290 					primaryVolumeDescriptor = *primary;
291 					break;
292 				}
293 
294 				case TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER:
295 					break;
296 
297 				case TAGID_VOLUME_DESCRIPTOR_POINTER:
298 					break;
299 
300 				case TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR:
301 				{
302 					implementation_use_descriptor *impUse = reinterpret_cast<implementation_use_descriptor*>(tag);
303 					PDUMP(impUse);
304 					// Check for a matching implementation id string (note that the
305 					// revision version is not checked)
306 					if (impUse->tag().init_check(block) == B_OK
307 					    && impUse->implementation_id().matches(kLogicalVolumeInfoId201))
308 					{
309 							foundUdfImplementationUseDescriptor = true;
310 					}
311 					break;
312 				}
313 
314 				case TAGID_PARTITION_DESCRIPTOR:
315 				{
316 					partition_descriptor *partition = reinterpret_cast<partition_descriptor*>(tag);
317 					PDUMP(partition);
318 					if (partition->tag().init_check(block) == B_OK) {
319 						// Check for a previously discovered partition descriptor with
320 						// the same number as this partition. If found, keep the one with
321 						// the higher vds number.
322 						bool foundDuplicate = false;
323 						int num;
324 						for (num = 0; num < uniquePartitions; num++) {
325 							if (partitionDescriptors[num].partition_number()
326 							    == partition->partition_number())
327 							{
328 								foundDuplicate = true;
329 								if (partitionDescriptors[num].vds_number()
330 								    < partition->vds_number())
331 								{
332 									partitionDescriptors[num] = *partition;
333 									PRINT(("Replacing previous partition #%d (vds_number: %ld) with "
334 									       "new partition #%d (vds_number: %ld)\n",
335 									       partitionDescriptors[num].partition_number(),
336 									       partitionDescriptors[num].vds_number(),
337 									       partition->partition_number(),
338 									       partition->vds_number()));
339 								}
340 								break;
341 							}
342 						}
343 						// If we didn't find a duplicate, see if we have any open descriptor
344 						// spaces left.
345 						if (!foundDuplicate) {
346 							if (num < kMaxPartitionDescriptors) {
347 								// At least one more partition descriptor allowed
348 								partitionDescriptors[num] = *partition;
349 								uniquePartitions++;
350 								PRINT(("Adding partition #%d (vds_number: %ld)\n",
351 								       partition->partition_number(),
352 								       partition->vds_number()));
353 							} else {
354 								// We've found more than kMaxPartitionDescriptor uniquely-
355 								// numbered partitions. So, search through the partitions
356 								// we already have again, this time just looking for a
357 								// partition with a lower vds number. If we find one,
358 								// replace it with this one. If we don't, scream bloody
359 								// murder.
360 								bool foundReplacement = false;
361 								for (int j = 0; j < uniquePartitions; j++) {
362 									if (partitionDescriptors[j].vds_number()
363 									    < partition->vds_number())
364 									{
365 										foundReplacement = true;
366 										partitionDescriptors[j] = *partition;
367 										PRINT(("Replacing partition #%d (vds_number: %ld) "
368 										       "with partition #%d (vds_number: %ld)\n",
369 										       partitionDescriptors[j].partition_number(),
370 										       partitionDescriptors[j].vds_number(),
371 										       partition->partition_number(),
372 										       partition->vds_number()));
373 										break;
374 									}
375 								}
376 								if (!foundReplacement) {
377 									PRINT(("Found more than kMaxPartitionDescriptors == %d "
378 									       "unique partition descriptors!\n",
379 									       kMaxPartitionDescriptors));
380 									error = B_BAD_VALUE;
381 									break;
382 								}
383 							}
384 						}
385 					}
386 					break;
387 				}
388 
389 				case TAGID_LOGICAL_VOLUME_DESCRIPTOR:
390 				{
391 					logical_volume_descriptor *logical = reinterpret_cast<logical_volume_descriptor*>(tag);
392 					PDUMP(logical);
393 					if (foundLogicalVolumeDescriptor) {
394 						// Keep the vd with the highest vds_number
395 						if (logicalVolumeDescriptor.vds_number() < logical->vds_number())
396 							logicalVolumeDescriptor = *logical;
397 					} else {
398 						logicalVolumeDescriptor = *logical;
399 						foundLogicalVolumeDescriptor = true;
400 					}
401 					break;
402 				}
403 
404 				case TAGID_UNALLOCATED_SPACE_DESCRIPTOR:
405 				{
406 					unallocated_space_descriptor *unallocated = reinterpret_cast<unallocated_space_descriptor*>(tag);
407 					PDUMP(unallocated);
408 					foundUnallocatedSpaceDescriptor = true;
409 					(void)unallocated;	// kill the warning
410 					break;
411 				}
412 
413 				case TAGID_TERMINATING_DESCRIPTOR:
414 				{
415 					terminating_descriptor *terminating = reinterpret_cast<terminating_descriptor*>(tag);
416 					PDUMP(terminating);
417 					(void)terminating;	// kill the warning
418 					break;
419 				}
420 
421 				case TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR:
422 					// Not found in this descriptor sequence
423 					break;
424 
425 				default:
426 					break;
427 
428 			}
429 		}
430 	}
431 
432 	PRINT(("found %d unique partition%s\n", uniquePartitions,
433 	       (uniquePartitions == 1 ? "" : "s")));
434 
435 	if (!error && !foundUdfImplementationUseDescriptor) {
436 		INFORM(("WARNING: no valid udf implementation use descriptor found\n"));
437 	}
438 	if (!error)
439 		error = foundLogicalVolumeDescriptor
440 		        && foundUnallocatedSpaceDescriptor
441 		        ? B_OK : B_ERROR;
442 	if (!error)
443 		error = uniquePartitions >= 1 ? B_OK : B_ERROR;
444 	if (!error)
445 		partitionDescriptorCount = uniquePartitions;
446 
447 	RETURN(error);
448 }
449 
450 /*! \brief Walks the integrity sequence in the extent given by \a descriptorSequence.
451 
452 	\return
453 	- \c B_OK: Success. the sequence was terminated by a valid, closed
454 	           integrity descriptor.
455 	- \c B_ENTRY_NOT_FOUND: The sequence was empty.
456 	- (other error code): The sequence was non-empty and did not end in a valid,
457                           closed integrity descriptor.
458 */
459 static status_t
460 walk_integrity_sequence(int device, uint32 blockSize, uint32 blockShift,
461                         extent_address descriptorSequence, uint32 sequenceNumber)
462 {
463 	DEBUG_INIT_ETC(NULL, ("descriptorSequence.loc:%ld, descriptorSequence.len:%ld",
464 	           descriptorSequence.location(), descriptorSequence.length()));
465 	uint32 count = descriptorSequence.length() >> blockShift;
466 
467 	bool lastDescriptorWasClosed = false;
468 	uint16 highestMinimumUDFReadRevision = 0x0000;
469 	status_t error = count > 0 ? B_OK : B_ENTRY_NOT_FOUND;
470 	for (uint32 i = 0; error == B_OK && i < count; i++)
471 	{
472 		off_t block = descriptorSequence.location()+i;
473 		off_t address = block << blockShift;
474 		MemoryChunk chunk(blockSize);
475 		descriptor_tag *tag = NULL;
476 
477 		PRINT(("integrity descriptor #%ld:%ld (block %Ld):\n", sequenceNumber, i, block));
478 
479 		status_t loopError = chunk.InitCheck();
480 		if (!loopError) {
481 			ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize);
482 			loopError = check_size_error(bytesRead, blockSize);
483 			if (loopError) {
484 				PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n",
485 				       block, address, blockSize, bytesRead));
486 			}
487 		}
488 		if (!loopError) {
489 			tag = reinterpret_cast<descriptor_tag *>(chunk.Data());
490 			loopError = tag->init_check(block);
491 		}
492 		if (!loopError) {
493 			// Check the descriptor type and see if it's closed.
494 			loopError = tag->id() == TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR
495 			            ? B_OK : B_BAD_DATA;
496 			if (!loopError) {
497 				logical_volume_integrity_descriptor *descriptor =
498 					reinterpret_cast<logical_volume_integrity_descriptor*>(chunk.Data());
499 				PDUMP(descriptor);
500 				lastDescriptorWasClosed = descriptor->integrity_type() == INTEGRITY_CLOSED;
501 				if (lastDescriptorWasClosed) {
502 					uint16 minimumRevision = descriptor->minimum_udf_read_revision();
503 					if (minimumRevision > highestMinimumUDFReadRevision) {
504 						highestMinimumUDFReadRevision = minimumRevision;
505 					} else if (minimumRevision < highestMinimumUDFReadRevision) {
506 						INFORM(("WARNING: found decreasing minimum udf read revision in integrity "
507 						        "sequence (last highest: 0x%04x, current: 0x%04x); using higher "
508 						        "revision.\n", highestMinimumUDFReadRevision, minimumRevision));
509 					}
510 				}
511 
512 				// Check a continuation extent if necessary. Note that this effectively
513 				// ends our search through this extent
514 				extent_address &next = descriptor->next_integrity_extent();
515 				if (next.length() > 0) {
516 					status_t nextError = walk_integrity_sequence(device, blockSize, blockShift,
517 					                                             next, sequenceNumber+1);
518 					if (nextError && nextError != B_ENTRY_NOT_FOUND) {
519 						// Continuation proved invalid
520 						error = nextError;
521 						break;
522 					} else {
523 						// Either the continuation was valid or empty; either way,
524 						// we're done searching.
525 						break;
526 					}
527 				}
528 			} else {
529 				PDUMP(tag);
530 			}
531 		}
532 		// If we hit an error on the first item, consider the extent empty,
533 		// otherwise just break out of the loop and assume part of the
534 		// extent is unrecorded
535 		if (loopError) {
536 			if (i == 0)
537 				error = B_ENTRY_NOT_FOUND;
538 			else
539 				break;
540 		}
541 	}
542 	if (!error)
543 		error = lastDescriptorWasClosed ? B_OK : B_BAD_DATA;
544 	if (error == B_OK
545 		&& highestMinimumUDFReadRevision > UDF_MAX_READ_REVISION) {
546 		error = B_ERROR;
547 		FATAL(("found udf revision 0x%x more than max 0x%x\n",
548 			highestMinimumUDFReadRevision, UDF_MAX_READ_REVISION));
549 	}
550 	RETURN(error);
551 }
552