xref: /haiku/src/add-ons/kernel/file_systems/udf/Recognition.cpp (revision d5cd5d63ff0ad395989db6cf4841a64d5b545d1d)
1 #include "Recognition.h"
2 
3 #include "CS0String.h"
4 #include "MemoryChunk.h"
5 
6 using namespace Udf;
7 
8 //------------------------------------------------------------------------------
9 // forward declarations
10 //------------------------------------------------------------------------------
11 
12 static status_t
13 walk_volume_recognition_sequence(int device, off_t offset,
14                                  uint32 blockSize,
15                                  uint32 blockShift);
16 static status_t
17 walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length,
18                                         uint32 blockSize, uint32 blockShift,
19                                         udf_logical_descriptor &logicalVolumeDescriptor,
20 							            udf_partition_descriptor partitionDescriptors[],
21 							            uint8 &partitionDescriptorCount);
22 static status_t
23 walk_volume_descriptor_sequence(udf_extent_address descriptorSequence,
24 								int device, uint32 blockSize, uint32 blockShift,
25 							    udf_logical_descriptor &logicalVolumeDescriptor,
26 							    udf_partition_descriptor partitionDescriptors[],
27 							    uint8 &partitionDescriptorCount);
28 
29 //------------------------------------------------------------------------------
30 // externally visible functions
31 //------------------------------------------------------------------------------
32 
33 status_t
34 Udf::udf_recognize(int device, off_t offset, off_t length, uint32 blockSize,
35                    uint32 &blockShift, udf_logical_descriptor &logicalVolumeDescriptor,
36 				   udf_partition_descriptor partitionDescriptors[],
37 				   uint8 &partitionDescriptorCount)
38 {
39 	DEBUG_INIT_ETC(CF_PRIVATE, NULL, ("device: %d, offset: %Ld, length: %Ld, "
40 	               "blockSize: %ld, [...descriptors, etc...]", device, offset,
41 	               length, blockSize));
42 
43 	// Check the block size
44 	uint32 bitCount = 0;
45 	for (int i = 0; i < 32; i++) {
46 		// Zero out all bits except bit i
47 		uint32 block = blockSize & (uint32(1) << i);
48 		if (block) {
49 			if (++bitCount > 1) {
50 				PRINT(("Block size must be a power of two! (blockSize = %ld)\n", blockSize));
51 				RETURN(B_BAD_VALUE);
52 			} else {
53 				blockShift = i;
54 				PRINT(("blockShift: %ld\n", blockShift));
55 			}
56 		}
57 	}
58 
59 	// Check for a valid volume recognition sequence
60 	status_t error = walk_volume_recognition_sequence(device, offset, blockSize, blockShift);
61 
62 	// Now hunt down a volume descriptor sequence from one of
63 	// the anchor volume pointers (if there are any).
64 	if (!error) {
65 		error = walk_anchor_volume_descriptor_sequences(device, offset, length,
66 		                                              blockSize, blockShift,
67 		                                              logicalVolumeDescriptor,
68 		                                              partitionDescriptors,
69 		                                              partitionDescriptorCount);
70 	}
71 	RETURN(error);
72 }
73 
74 status_t
75 Udf::udf_recognize(int device, off_t offset, off_t length, uint32 blockSize,
76                    char *volumeName)
77 {
78 	DEBUG_INIT_ETC(CF_PRIVATE, NULL, ("device: %d, offset: %Ld, length: %Ld, "
79 	               "blockSize: %ld, volumeName: %p", device, offset, length,
80 	               blockSize, volumeName));
81 	udf_logical_descriptor logicalVolumeDescriptor;
82 	udf_partition_descriptor partitionDescriptors[Udf::kMaxPartitionDescriptors];
83 	uint8 partitionDescriptorCount;
84 	uint32 blockShift;
85 	status_t error = udf_recognize(device, offset, length, blockSize, blockShift,
86 	                               logicalVolumeDescriptor, partitionDescriptors,
87 	                               partitionDescriptorCount);
88 	if (!error && volumeName) {
89 		CS0String name(logicalVolumeDescriptor.logical_volume_identifier());
90 		strcpy(volumeName, name.String());
91 	}
92 	RETURN(error);
93 }
94 
95 //------------------------------------------------------------------------------
96 // local functions
97 //------------------------------------------------------------------------------
98 
99 static
100 status_t
101 walk_volume_recognition_sequence(int device, off_t offset, uint32 blockSize, uint32 blockShift)
102 {
103 	DEBUG_INIT(CF_PRIVATE, NULL);
104 	// vrs starts at block 16. Each volume structure descriptor (vsd)
105 	// should be one block long. We're expecting to find 0 or more iso9660
106 	// vsd's followed by some ECMA-167 vsd's.
107 	MemoryChunk chunk(blockSize);
108 	status_t error = chunk.InitCheck();
109 	if (!error) {
110 		bool foundISO = false;
111 		bool foundExtended = false;
112 		bool foundECMA167 = false;
113 		bool foundECMA168 = false;
114 		bool foundBoot = false;
115 		for (uint32 block = 16; true; block++) {
116 	    	PRINT(("block %ld: ", block))
117 			off_t address = (offset + block) << blockShift;
118 			ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize);
119 			if (bytesRead == (ssize_t)blockSize)
120 		    {
121 		    	udf_volume_structure_descriptor_header* descriptor =
122 		    	  reinterpret_cast<udf_volume_structure_descriptor_header*>(chunk.Data());
123 				if (descriptor->id_matches(kVSDID_ISO)) {
124 					SIMPLE_PRINT(("found ISO9660 descriptor\n"));
125 					foundISO = true;
126 				} else if (descriptor->id_matches(kVSDID_BEA)) {
127 					SIMPLE_PRINT(("found BEA descriptor\n"));
128 					foundExtended = true;
129 				} else if (descriptor->id_matches(kVSDID_TEA)) {
130 					SIMPLE_PRINT(("found TEA descriptor\n"));
131 					foundExtended = true;
132 				} else if (descriptor->id_matches(kVSDID_ECMA167_2)) {
133 					SIMPLE_PRINT(("found ECMA-167 rev 2 descriptor\n"));
134 					foundECMA167 = true;
135 				} else if (descriptor->id_matches(kVSDID_ECMA167_3)) {
136 					SIMPLE_PRINT(("found ECMA-167 rev 3 descriptor\n"));
137 					foundECMA167 = true;
138 				} else if (descriptor->id_matches(kVSDID_BOOT)) {
139 					SIMPLE_PRINT(("found boot descriptor\n"));
140 					foundBoot = true;
141 				} else if (descriptor->id_matches(kVSDID_ECMA168)) {
142 					SIMPLE_PRINT(("found ECMA-168 descriptor\n"));
143 					foundECMA168 = true;
144 				} else {
145 					SIMPLE_PRINT(("found invalid descriptor, id = `%.5s'\n", descriptor->id));
146 					break;
147 				}
148 			} else {
149 				SIMPLE_PRINT(("read_pos(pos:%Ld, len:%ld) failed with: 0x%lx\n", address,
150 				        blockSize, bytesRead));
151 				break;
152 			}
153 		}
154 
155 		// If we find an ECMA-167 descriptor, OR if we find a beginning
156 		// or terminating extended area descriptor with NO ECMA-168
157 		// descriptors, we return B_OK to signal that we should go
158 		// looking for valid anchors.
159 		error = foundECMA167 || (foundExtended && !foundECMA168) ? B_OK : B_ERROR;
160 	}
161 
162 	RETURN(error);
163 }
164 
165 static
166 status_t
167 walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length,
168                                         uint32 blockSize, uint32 blockShift,
169                                         udf_logical_descriptor &logicalVolumeDescriptor,
170 							            udf_partition_descriptor partitionDescriptors[],
171 							            uint8 &partitionDescriptorCount)
172 {
173 	DEBUG_INIT(CF_PRIVATE, NULL);
174 	const uint8 avds_location_count = 4;
175 	const off_t avds_locations[avds_location_count] = {
176 														256,
177 	                                                    length-1-256,
178 	                                                    length-1,
179 	                                                    512,
180 	                                                  };
181 	bool found_vds = false;
182 	for (int32 i = 0; i < avds_location_count; i++) {
183 		off_t block = avds_locations[i];
184 		off_t address = (offset + block) << blockShift;
185 		MemoryChunk chunk(blockSize);
186 		udf_anchor_descriptor *anchor = NULL;
187 
188 		status_t anchorErr = chunk.InitCheck();
189 		if (!anchorErr) {
190 			ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize);
191 			anchorErr = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR;
192 			if (anchorErr) {
193 				PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n",
194 				       block, address, blockSize, bytesRead));
195 			}
196 		}
197 		if (!anchorErr) {
198 			anchor = reinterpret_cast<udf_anchor_descriptor*>(chunk.Data());
199 			anchorErr = anchor->tag().init_check(block+offset);
200 			if (anchorErr) {
201 				PRINT(("block %Ld: invalid anchor\n", block));
202 			} else {
203 				PRINT(("block %Ld: valid anchor\n", block));
204 			}
205 		}
206 		if (!anchorErr) {
207 			PRINT(("block %Ld: anchor:\n", block));
208 			PDUMP(anchor);
209 			// Found an avds, so try the main sequence first, then
210 			// the reserve sequence if the main one fails.
211 			anchorErr = walk_volume_descriptor_sequence(anchor->main_vds(), device,
212 														blockSize, blockShift,
213 			                                            logicalVolumeDescriptor,
214 			                                            partitionDescriptors,
215 			                                            partitionDescriptorCount);
216 			if (anchorErr)
217 				anchorErr = walk_volume_descriptor_sequence(anchor->reserve_vds(), device,
218 															blockSize, blockShift,
219 			                                            	logicalVolumeDescriptor,
220 			                                            	partitionDescriptors,
221 			                                            	partitionDescriptorCount);
222 		}
223 		if (!anchorErr) {
224 			PRINT(("block %Ld: found valid vds\n", avds_locations[i]));
225 			found_vds = true;
226 			break;
227 		} else {
228 			// Both failed, so loop around and try another avds
229 			PRINT(("block %Ld: vds search failed\n", avds_locations[i]));
230 		}
231 	}
232 	status_t error = found_vds ? B_OK : B_ERROR;
233 	RETURN(error);
234 }
235 
236 static
237 status_t
238 walk_volume_descriptor_sequence(udf_extent_address descriptorSequence,
239 								int device, uint32 blockSize, uint32 blockShift,
240 							    udf_logical_descriptor &logicalVolumeDescriptor,
241 							    udf_partition_descriptor partitionDescriptors[],
242 							    uint8 &partitionDescriptorCount)
243 {
244 	DEBUG_INIT_ETC(CF_PRIVATE, NULL, ("descriptorSequence.loc:%ld, descriptorSequence.len:%ld",
245 	           descriptorSequence.location(), descriptorSequence.length()));
246 	uint32 count = descriptorSequence.length() >> blockShift;
247 
248 	bool foundLogicalVolumeDescriptor = false;
249 	uint8 uniquePartitions = 0;
250 	status_t error = B_OK;
251 
252 	for (uint32 i = 0; i < count; i++)
253 	{
254 		off_t block = descriptorSequence.location()+i;
255 		off_t address = block << blockShift;
256 		MemoryChunk chunk(blockSize);
257 		udf_tag *tag = NULL;
258 
259 		PRINT(("descriptor #%ld (block %Ld):\n", i, block));
260 
261 		status_t loopErr = chunk.InitCheck();
262 		if (!loopErr) {
263 			ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize);
264 			loopErr = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR;
265 			if (loopErr) {
266 				PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n",
267 				       block, address, blockSize, bytesRead));
268 			}
269 		}
270 		if (!loopErr) {
271 			tag = reinterpret_cast<udf_tag*>(chunk.Data());
272 			loopErr = tag->init_check(block);
273 		}
274 		if (!loopErr) {
275 			// Now decide what type of descriptor we have
276 			switch (tag->id()) {
277 				case TAGID_UNDEFINED:
278 					break;
279 
280 				case TAGID_PRIMARY_VOLUME_DESCRIPTOR:
281 				{
282 					udf_primary_descriptor *primary = reinterpret_cast<udf_primary_descriptor*>(tag);
283 					PDUMP(primary);
284 					(void)primary;	// kill the warning
285 					break;
286 				}
287 
288 				case TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER:
289 					break;
290 
291 				case TAGID_VOLUME_DESCRIPTOR_POINTER:
292 					break;
293 
294 				case TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR:
295 				{
296 					udf_implementation_use_descriptor *imp_use = reinterpret_cast<udf_implementation_use_descriptor*>(tag);
297 					PDUMP(imp_use);
298 					(void)imp_use;	// kill the warning
299 					break;
300 				}
301 
302 				case TAGID_PARTITION_DESCRIPTOR:
303 				{
304 					udf_partition_descriptor *partition = reinterpret_cast<udf_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 < Udf::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 					udf_logical_descriptor *logical = reinterpret_cast<udf_logical_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 						DUMP(logicalVolumeDescriptor);
389 					}
390 					break;
391 				}
392 
393 				case TAGID_UNALLOCATED_SPACE_DESCRIPTOR:
394 				{
395 					udf_unallocated_space_descriptor *unallocated = reinterpret_cast<udf_unallocated_space_descriptor*>(tag);
396 					PDUMP(unallocated);
397 					(void)unallocated;	// kill the warning
398 					break;
399 				}
400 
401 				case TAGID_TERMINATING_DESCRIPTOR:
402 				{
403 					udf_terminating_descriptor *terminating = reinterpret_cast<udf_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)
424 		error = foundLogicalVolumeDescriptor ? B_OK : B_ERROR;
425 	if (!error)
426 		error = uniquePartitions >= 1 ? B_OK : B_ERROR;
427 	if (!error)
428 		partitionDescriptorCount = uniquePartitions;
429 
430 	RETURN(error);
431 }
432 
433