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