xref: /haiku/src/add-ons/kernel/file_systems/udf/Recognition.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 		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 							foundUdfImplementationUseDescriptor = true;
308 					}
309 					break;
310 				}
311 
312 				case TAGID_PARTITION_DESCRIPTOR:
313 				{
314 					partition_descriptor *partition = reinterpret_cast<partition_descriptor*>(tag);
315 					PDUMP(partition);
316 					if (partition->tag().init_check(block) == B_OK) {
317 						// Check for a previously discovered partition descriptor with
318 						// the same number as this partition. If found, keep the one with
319 						// the higher vds number.
320 						bool foundDuplicate = false;
321 						int num;
322 						for (num = 0; num < uniquePartitions; num++) {
323 							if (partitionDescriptors[num].partition_number()
324 							    == partition->partition_number()) {
325 								foundDuplicate = true;
326 								if (partitionDescriptors[num].vds_number()
327 								    < partition->vds_number()) {
328 									partitionDescriptors[num] = *partition;
329 									PRINT(("Replacing previous partition #%d (vds_number: %ld) with "
330 									       "new partition #%d (vds_number: %ld)\n",
331 									       partitionDescriptors[num].partition_number(),
332 									       partitionDescriptors[num].vds_number(),
333 									       partition->partition_number(),
334 									       partition->vds_number()));
335 								}
336 								break;
337 							}
338 						}
339 						// If we didn't find a duplicate, see if we have any open descriptor
340 						// spaces left.
341 						if (!foundDuplicate) {
342 							if (num < kMaxPartitionDescriptors) {
343 								// At least one more partition descriptor allowed
344 								partitionDescriptors[num] = *partition;
345 								uniquePartitions++;
346 								PRINT(("Adding partition #%d (vds_number: %ld)\n",
347 								       partition->partition_number(),
348 								       partition->vds_number()));
349 							} else {
350 								// We've found more than kMaxPartitionDescriptor uniquely-
351 								// numbered partitions. So, search through the partitions
352 								// we already have again, this time just looking for a
353 								// partition with a lower vds number. If we find one,
354 								// replace it with this one. If we don't, scream bloody
355 								// murder.
356 								bool foundReplacement = false;
357 								for (int j = 0; j < uniquePartitions; j++) {
358 									if (partitionDescriptors[j].vds_number()
359 									    < partition->vds_number()) {
360 										foundReplacement = true;
361 										partitionDescriptors[j] = *partition;
362 										PRINT(("Replacing partition #%d (vds_number: %ld) "
363 										       "with partition #%d (vds_number: %ld)\n",
364 										       partitionDescriptors[j].partition_number(),
365 										       partitionDescriptors[j].vds_number(),
366 										       partition->partition_number(),
367 										       partition->vds_number()));
368 										break;
369 									}
370 								}
371 								if (!foundReplacement) {
372 									PRINT(("Found more than kMaxPartitionDescriptors == %d "
373 									       "unique partition descriptors!\n",
374 									       kMaxPartitionDescriptors));
375 									error = B_BAD_VALUE;
376 									break;
377 								}
378 							}
379 						}
380 					}
381 					break;
382 				}
383 
384 				case TAGID_LOGICAL_VOLUME_DESCRIPTOR:
385 				{
386 					logical_volume_descriptor *logical = reinterpret_cast<logical_volume_descriptor*>(tag);
387 					PDUMP(logical);
388 					if (foundLogicalVolumeDescriptor) {
389 						// Keep the vd with the highest vds_number
390 						if (logicalVolumeDescriptor.vds_number() < logical->vds_number())
391 							logicalVolumeDescriptor = *logical;
392 					} else {
393 						logicalVolumeDescriptor = *logical;
394 						foundLogicalVolumeDescriptor = true;
395 					}
396 					break;
397 				}
398 
399 				case TAGID_UNALLOCATED_SPACE_DESCRIPTOR:
400 				{
401 					unallocated_space_descriptor *unallocated = reinterpret_cast<unallocated_space_descriptor*>(tag);
402 					PDUMP(unallocated);
403 					foundUnallocatedSpaceDescriptor = true;
404 					(void)unallocated;	// kill the warning
405 					break;
406 				}
407 
408 				case TAGID_TERMINATING_DESCRIPTOR:
409 				{
410 					terminating_descriptor *terminating = reinterpret_cast<terminating_descriptor*>(tag);
411 					PDUMP(terminating);
412 					(void)terminating;	// kill the warning
413 					break;
414 				}
415 
416 				case TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR:
417 					// Not found in this descriptor sequence
418 					break;
419 
420 				default:
421 					break;
422 
423 			}
424 		}
425 	}
426 
427 	PRINT(("found %d unique partition%s\n", uniquePartitions,
428 	       (uniquePartitions == 1 ? "" : "s")));
429 
430 	if (!error && !foundUdfImplementationUseDescriptor) {
431 		INFORM(("WARNING: no valid udf implementation use descriptor found\n"));
432 	}
433 	if (!error)
434 		error = foundLogicalVolumeDescriptor
435 		        && foundUnallocatedSpaceDescriptor
436 		        ? B_OK : B_ERROR;
437 	if (!error)
438 		error = uniquePartitions >= 1 ? B_OK : B_ERROR;
439 	if (!error)
440 		partitionDescriptorCount = uniquePartitions;
441 
442 	RETURN(error);
443 }
444 
445 /*! \brief Walks the integrity sequence in the extent given by \a descriptorSequence.
446 
447 	\return
448 	- \c B_OK: Success. the sequence was terminated by a valid, closed
449 	           integrity descriptor.
450 	- \c B_ENTRY_NOT_FOUND: The sequence was empty.
451 	- (other error code): The sequence was non-empty and did not end in a valid,
452                           closed integrity descriptor.
453 */
454 static status_t
455 walk_integrity_sequence(int device, uint32 blockSize, uint32 blockShift,
456                         extent_address descriptorSequence, uint32 sequenceNumber)
457 {
458 	DEBUG_INIT_ETC(NULL, ("descriptorSequence.loc:%ld, descriptorSequence.len:%ld",
459 	           descriptorSequence.location(), descriptorSequence.length()));
460 	uint32 count = descriptorSequence.length() >> blockShift;
461 
462 	bool lastDescriptorWasClosed = false;
463 	uint16 highestMinimumUDFReadRevision = 0x0000;
464 	status_t error = count > 0 ? B_OK : B_ENTRY_NOT_FOUND;
465 	for (uint32 i = 0; error == B_OK && i < count; i++) {
466 		off_t block = descriptorSequence.location()+i;
467 		off_t address = block << blockShift;
468 		MemoryChunk chunk(blockSize);
469 		descriptor_tag *tag = NULL;
470 
471 		PRINT(("integrity descriptor #%ld:%ld (block %Ld):\n", sequenceNumber, i, block));
472 
473 		status_t loopError = chunk.InitCheck();
474 		if (!loopError) {
475 			ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize);
476 			loopError = check_size_error(bytesRead, blockSize);
477 			if (loopError) {
478 				PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n",
479 				       block, address, blockSize, bytesRead));
480 			}
481 		}
482 		if (!loopError) {
483 			tag = reinterpret_cast<descriptor_tag *>(chunk.Data());
484 			loopError = tag->init_check(block);
485 		}
486 		if (!loopError) {
487 			// Check the descriptor type and see if it's closed.
488 			loopError = tag->id() == TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR
489 			            ? B_OK : B_BAD_DATA;
490 			if (!loopError) {
491 				logical_volume_integrity_descriptor *descriptor =
492 					reinterpret_cast<logical_volume_integrity_descriptor*>(chunk.Data());
493 				PDUMP(descriptor);
494 				lastDescriptorWasClosed = descriptor->integrity_type() == INTEGRITY_CLOSED;
495 				if (lastDescriptorWasClosed) {
496 					uint16 minimumRevision = descriptor->minimum_udf_read_revision();
497 					if (minimumRevision > highestMinimumUDFReadRevision) {
498 						highestMinimumUDFReadRevision = minimumRevision;
499 					} else if (minimumRevision < highestMinimumUDFReadRevision) {
500 						INFORM(("WARNING: found decreasing minimum udf read revision in integrity "
501 						        "sequence (last highest: 0x%04x, current: 0x%04x); using higher "
502 						        "revision.\n", highestMinimumUDFReadRevision, minimumRevision));
503 					}
504 				}
505 
506 				// Check a continuation extent if necessary. Note that this effectively
507 				// ends our search through this extent
508 				extent_address &next = descriptor->next_integrity_extent();
509 				if (next.length() > 0) {
510 					status_t nextError = walk_integrity_sequence(device, blockSize, blockShift,
511 					                                             next, sequenceNumber+1);
512 					if (nextError && nextError != B_ENTRY_NOT_FOUND) {
513 						// Continuation proved invalid
514 						error = nextError;
515 						break;
516 					} else {
517 						// Either the continuation was valid or empty; either way,
518 						// we're done searching.
519 						break;
520 					}
521 				}
522 			} else {
523 				PDUMP(tag);
524 			}
525 		}
526 		// If we hit an error on the first item, consider the extent empty,
527 		// otherwise just break out of the loop and assume part of the
528 		// extent is unrecorded
529 		if (loopError) {
530 			if (i == 0)
531 				error = B_ENTRY_NOT_FOUND;
532 			else
533 				break;
534 		}
535 	}
536 	if (!error)
537 		error = lastDescriptorWasClosed ? B_OK : B_BAD_DATA;
538 	if (error == B_OK
539 		&& highestMinimumUDFReadRevision > UDF_MAX_READ_REVISION) {
540 		error = B_ERROR;
541 		FATAL(("found udf revision 0x%x more than max 0x%x\n",
542 			highestMinimumUDFReadRevision, UDF_MAX_READ_REVISION));
543 	}
544 	RETURN(error);
545 }
546