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