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