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