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