xref: /haiku/src/add-ons/kernel/file_systems/iso9660/iso9660.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  * Copyright 1999, Be Incorporated.   All Rights Reserved.
3  * This file may be used under the terms of the Be Sample Code License.
4  *
5  * Copyright 2001, pinc Software.  All Rights Reserved.
6  */
7 
8 
9 #include "iso9660.h"
10 
11 #include <ctype.h>
12 
13 #ifndef FS_SHELL
14 #	include <dirent.h>
15 #	include <fcntl.h>
16 #	include <stdlib.h>
17 #	include <string.h>
18 #	include <sys/stat.h>
19 #	include <time.h>
20 #	include <unistd.h>
21 
22 #	include <ByteOrder.h>
23 #	include <Drivers.h>
24 #	include <KernelExport.h>
25 #	include <fs_cache.h>
26 #endif
27 
28 #include "rock_ridge.h"
29 
30 //#define TRACE_ISO9660 1
31 #if TRACE_ISO9660
32 #	define TRACE(x) dprintf x
33 #else
34 #	define TRACE(x) ;
35 #endif
36 
37 
38 // Just needed here
39 static status_t unicode_to_utf8(const char	*src, int32	*srcLen, char *dst,
40 	int32 *dstLen);
41 
42 
43 // ISO9660 should start with this string
44 const char *kISO9660IDString = "CD001";
45 
46 
47 static int
48 get_device_block_size(int fd)
49 {
50 	device_geometry geometry;
51 
52 	if (ioctl(fd, B_GET_GEOMETRY, &geometry, sizeof(device_geometry)) < 0)  {
53 		struct stat st;
54 		if (fstat(fd, &st) < 0 || S_ISDIR(st.st_mode))
55 			return 0;
56 
57 		return 512;   /* just assume it's a plain old file or something */
58 	}
59 
60 	return geometry.bytes_per_sector;
61 }
62 
63 
64 // From EncodingComversions.cpp
65 
66 // Pierre's (modified) Uber Macro
67 
68 // NOTE: iso9660 seems to store the unicode text in big-endian form
69 #define u_to_utf8(str, uni_str)\
70 {\
71 	if ((B_BENDIAN_TO_HOST_INT16(uni_str[0])&0xff80) == 0)\
72 		*str++ = B_BENDIAN_TO_HOST_INT16(*uni_str++);\
73 	else if ((B_BENDIAN_TO_HOST_INT16(uni_str[0])&0xf800) == 0) {\
74 		str[0] = 0xc0|(B_BENDIAN_TO_HOST_INT16(uni_str[0])>>6);\
75 		str[1] = 0x80|(B_BENDIAN_TO_HOST_INT16(*uni_str++)&0x3f);\
76 		str += 2;\
77 	} else if ((B_BENDIAN_TO_HOST_INT16(uni_str[0])&0xfc00) != 0xd800) {\
78 		str[0] = 0xe0|(B_BENDIAN_TO_HOST_INT16(uni_str[0])>>12);\
79 		str[1] = 0x80|((B_BENDIAN_TO_HOST_INT16(uni_str[0])>>6)&0x3f);\
80 		str[2] = 0x80|(B_BENDIAN_TO_HOST_INT16(*uni_str++)&0x3f);\
81 		str += 3;\
82 	} else {\
83 		int   val;\
84 		val = ((B_BENDIAN_TO_HOST_INT16(uni_str[0])-0xd7c0)<<10) | (B_BENDIAN_TO_HOST_INT16(uni_str[1])&0x3ff);\
85 		str[0] = 0xf0 | (val>>18);\
86 		str[1] = 0x80 | ((val>>12)&0x3f);\
87 		str[2] = 0x80 | ((val>>6)&0x3f);\
88 		str[3] = 0x80 | (val&0x3f);\
89 		uni_str += 2; str += 4;\
90 	}\
91 }
92 
93 
94 static status_t
95 unicode_to_utf8(const char	*src, int32	*srcLen, char *dst, int32 *dstLen)
96 {
97 	int32 srcLimit = *srcLen;
98 	int32 dstLimit = *dstLen;
99 	int32 srcCount = 0;
100 	int32 dstCount = 0;
101 
102 	for (srcCount = 0; srcCount < srcLimit; srcCount += 2) {
103 		uint16 *UNICODE = (uint16 *)&src[srcCount];
104 		uint8 utf8[4];
105 		uint8 *UTF8 = utf8;
106 		int32 utf8Len;
107 		int32 j;
108 
109 		u_to_utf8(UTF8, UNICODE);
110 
111 		utf8Len = UTF8 - utf8;
112 		if (dstCount + utf8Len > dstLimit)
113 			break;
114 
115 		for (j = 0; j < utf8Len; j++)
116 			dst[dstCount + j] = utf8[j];
117 		dstCount += utf8Len;
118 	}
119 
120 	*srcLen = srcCount;
121 	*dstLen = dstCount;
122 
123 	return dstCount > 0 ? B_NO_ERROR : B_ERROR;
124 }
125 
126 
127 static void
128 sanitize_iso_name(iso9660_inode* node, bool removeTrailingPoints)
129 {
130 	// Get rid of semicolons, which are used to delineate file versions.
131 	if (char* semi = strchr(node->name, ';')) {
132 		semi[0] = '\0';
133 		node->name_length = semi - node->name;
134 	}
135 
136 	if (removeTrailingPoints) {
137 		// Get rid of trailing points
138 		if (node->name_length > 2 && node->name[node->name_length - 1] == '.')
139 			node->name[--node->name_length] = '\0';
140 	}
141 }
142 
143 
144 static int
145 init_volume_date(ISOVolDate *date, char *buffer)
146 {
147 	memcpy(date, buffer, ISO_VOL_DATE_SIZE);
148 	return 0;
149 }
150 
151 
152 static int
153 init_node_date(ISORecDate *date, char *buffer)
154 {
155 	memcpy(date, buffer, sizeof(struct ISORecDate));
156 	return 0;
157 }
158 
159 
160 static status_t
161 InitVolDesc(iso9660_volume *volume, char *buffer)
162 {
163 	TRACE(("InitVolDesc - ENTER\n"));
164 
165 	volume->volDescType = *(uint8 *)buffer;
166 	buffer += sizeof(volume->volDescType);
167 
168 	const size_t kStdIDStringLen = sizeof(volume->stdIDString) - 1;
169 	volume->stdIDString[kStdIDStringLen] = '\0';
170 	strncpy(volume->stdIDString, buffer, kStdIDStringLen);
171 	buffer += kStdIDStringLen;
172 
173 	volume->volDescVersion = *(uint8 *)buffer;
174 	buffer += sizeof(volume->volDescVersion);
175 
176 	buffer += sizeof(volume->unused1); // skip unused 8th byte
177 
178 	const size_t kSystemIDStringLen = sizeof(volume->systemIDString) - 1;
179 	volume->systemIDString[kSystemIDStringLen] = '\0';
180 	strncpy(volume->systemIDString, buffer, kSystemIDStringLen);
181 	buffer += kSystemIDStringLen;
182 	TRACE(("InitVolDesc - system id string is %s\n", volume->systemIDString));
183 
184 	const size_t kVolIDStringLen = sizeof(volume->volIDString) - 1;
185 	volume->volIDString[kVolIDStringLen] = '\0';
186 	strncpy(volume->volIDString, buffer, kVolIDStringLen);
187 	buffer += kVolIDStringLen;
188 	TRACE(("InitVolDesc - volume id string is %s\n", volume->volIDString));
189 
190 	buffer += sizeof(volume->unused2) - 1; // skip unused 73-80 bytes
191 
192 	volume->volSpaceSize[LSB_DATA] = *(uint32 *)buffer;
193 	buffer += sizeof(volume->volSpaceSize[LSB_DATA]);
194 	volume->volSpaceSize[MSB_DATA] = *(uint32 *)buffer;
195 	buffer += sizeof(volume->volSpaceSize[MSB_DATA]);
196 
197 	buffer += sizeof(volume->unused3) - 1; // skip unused 89-120 bytes
198 
199 	volume->volSetSize[LSB_DATA] = *(uint16*)buffer;
200 	buffer += sizeof(volume->volSetSize[LSB_DATA]);
201 	volume->volSetSize[MSB_DATA] = *(uint16*)buffer;
202 	buffer += sizeof(volume->volSetSize[MSB_DATA]);
203 
204 	volume->volSeqNum[LSB_DATA] = *(uint16*)buffer;
205 	buffer += sizeof(volume->volSeqNum[LSB_DATA]);
206 	volume->volSeqNum[MSB_DATA] = *(uint16*)buffer;
207 	buffer += sizeof(volume->volSeqNum[MSB_DATA]);
208 
209 	volume->logicalBlkSize[LSB_DATA] = *(uint16*)buffer;
210 	buffer += sizeof(volume->logicalBlkSize[LSB_DATA]);
211 	volume->logicalBlkSize[MSB_DATA] = *(uint16*)buffer;
212 	buffer += sizeof(volume->logicalBlkSize[MSB_DATA]);
213 
214 	volume->pathTblSize[LSB_DATA] = *(uint32*)buffer;
215 	buffer += sizeof(volume->pathTblSize[LSB_DATA]);
216 	volume->pathTblSize[MSB_DATA] = *(uint32*)buffer;
217 	buffer += sizeof(volume->pathTblSize[MSB_DATA]);
218 
219 	volume->lPathTblLoc[LSB_DATA] = *(uint16*)buffer;
220 	buffer += sizeof(volume->lPathTblLoc[LSB_DATA]);
221 	volume->lPathTblLoc[MSB_DATA] = *(uint16*)buffer;
222 	buffer += sizeof(volume->lPathTblLoc[MSB_DATA]);
223 
224 	volume->optLPathTblLoc[LSB_DATA] = *(uint16*)buffer;
225 	buffer += sizeof(volume->optLPathTblLoc[LSB_DATA]);
226 	volume->optLPathTblLoc[MSB_DATA] = *(uint16*)buffer;
227 	buffer += sizeof(volume->optLPathTblLoc[MSB_DATA]);
228 
229 	volume->mPathTblLoc[LSB_DATA] = *(uint16*)buffer;
230 	buffer += sizeof(volume->mPathTblLoc[LSB_DATA]);
231 	volume->mPathTblLoc[MSB_DATA] = *(uint16*)buffer;
232 	buffer += sizeof(volume->mPathTblLoc[MSB_DATA]);
233 
234 	volume->optMPathTblLoc[LSB_DATA] = *(uint16*)buffer;
235 	buffer += sizeof(volume->optMPathTblLoc[LSB_DATA]);
236 	volume->optMPathTblLoc[MSB_DATA] = *(uint16*)buffer;
237 	buffer += sizeof(volume->optMPathTblLoc[MSB_DATA]);
238 
239 	// Fill in directory record.
240 	volume->joliet_level = 0;
241 	InitNode(volume, &volume->rootDirRec, buffer, NULL);
242 
243 	volume->rootDirRec.id = ISO_ROOTNODE_ID;
244 	buffer += ISO_ROOT_DIR_REC_SIZE;
245 
246 	const size_t kVolSetIDStringLen = sizeof(volume->volSetIDString) - 1;
247 	volume->volSetIDString[kVolSetIDStringLen] = '\0';
248 	strncpy(volume->volSetIDString, buffer, kVolSetIDStringLen);
249 	buffer += kVolSetIDStringLen;
250 	TRACE(("InitVolDesc - volume set id string is %s\n", volume->volSetIDString));
251 
252 	const size_t kPubIDStringLen = sizeof(volume->pubIDString) - 1;
253 	volume->pubIDString[kPubIDStringLen] = '\0';
254 	strncpy(volume->pubIDString, buffer, kPubIDStringLen);
255 	buffer += kPubIDStringLen;
256 	TRACE(("InitVolDesc - volume pub id string is %s\n", volume->pubIDString));
257 
258 	const size_t kDataPreparerLen = sizeof(volume->dataPreparer) - 1;
259 	volume->dataPreparer[kDataPreparerLen] = '\0';
260 	strncpy(volume->dataPreparer, buffer, kDataPreparerLen);
261 	buffer += kDataPreparerLen;
262 	TRACE(("InitVolDesc - volume dataPreparer string is %s\n", volume->dataPreparer));
263 
264 	const size_t kAppIDStringLen = sizeof(volume->appIDString) - 1;
265 	volume->appIDString[kAppIDStringLen] = '\0';
266 	strncpy(volume->appIDString, buffer, kAppIDStringLen);
267 	buffer += kAppIDStringLen;
268 	TRACE(("InitVolDesc - volume app id string is %s\n", volume->appIDString));
269 
270 	const size_t kCopyrightLen = sizeof(volume->copyright) - 1;
271 	volume->copyright[kCopyrightLen] = '\0';
272 	strncpy(volume->copyright, buffer, kCopyrightLen);
273 	buffer += kCopyrightLen;
274 	TRACE(("InitVolDesc - copyright is %s\n", volume->copyright));
275 
276 	const size_t kAbstractFNameLen = sizeof(volume->abstractFName) - 1;
277 	volume->abstractFName[kAbstractFNameLen] = '\0';
278 	strncpy(volume->abstractFName, buffer, kAbstractFNameLen);
279 	buffer += kAbstractFNameLen;
280 
281 	const size_t kBiblioFNameLen = sizeof(volume->biblioFName) - 1;
282 	volume->biblioFName[kBiblioFNameLen] = '\0';
283 	strncpy(volume->biblioFName, buffer, kBiblioFNameLen);
284 	buffer += kBiblioFNameLen;
285 
286 	init_volume_date(&volume->createDate, buffer);
287 	buffer += ISO_VOL_DATE_SIZE;
288 
289 	init_volume_date(&volume->modDate, buffer);
290 	buffer += ISO_VOL_DATE_SIZE;
291 
292 	init_volume_date(&volume->expireDate, buffer);
293 	buffer += ISO_VOL_DATE_SIZE;
294 
295 	init_volume_date(&volume->effectiveDate, buffer);
296 	buffer += ISO_VOL_DATE_SIZE;
297 
298 	volume->fileStructVers = *(uint8*)buffer;
299 	return B_OK;
300 }
301 
302 
303 static status_t
304 parse_rock_ridge(iso9660_volume* volume, iso9660_inode* node, char* buffer,
305 	char* end, bool relocated)
306 {
307 	// Now we're at the start of the rock ridge stuff
308 	char* altName = NULL;
309 	char* slName = NULL;
310 	char* newSlName = NULL;
311 	uint16 altNameSize = 0;
312 	uint16 slNameSize = 0;
313 	uint8 length = 0;
314 	bool done = false;
315 
316 	TRACE(("RR: Start of extensions at %p\n", buffer));
317 
318 	while (!done) {
319 		buffer += length;
320 		if (buffer + 2 >= end)
321 			break;
322 		length = *(uint8*)(buffer + 2);
323 		if (buffer + length > end)
324 			break;
325 		if (length == 0)
326 			break;
327 
328 		switch (((int)buffer[0] << 8) + buffer[1]) {
329 			// Stat structure stuff
330 			case 'PX':
331 			{
332 				uint8 bytePos = 3;
333 				TRACE(("RR: found PX, length %u\n", length));
334 				node->attr.pxVer = *(uint8*)(buffer + bytePos++);
335 
336 				// st_mode
337 				node->attr.stat[LSB_DATA].st_mode
338 					= *(mode_t*)(buffer + bytePos);
339 				bytePos += 4;
340 				node->attr.stat[MSB_DATA].st_mode
341 					= *(mode_t*)(buffer + bytePos);
342 				bytePos += 4;
343 
344 				// st_nlink
345 				node->attr.stat[LSB_DATA].st_nlink
346 					= *(nlink_t*)(buffer+bytePos);
347 				bytePos += 4;
348 				node->attr.stat[MSB_DATA].st_nlink
349 					= *(nlink_t*)(buffer + bytePos);
350 				bytePos += 4;
351 
352 				// st_uid
353 				node->attr.stat[LSB_DATA].st_uid
354 					= *(uid_t*)(buffer + bytePos);
355 				bytePos += 4;
356 				node->attr.stat[MSB_DATA].st_uid
357 					= *(uid_t*)(buffer + bytePos);
358 				bytePos += 4;
359 
360 				// st_gid
361 				node->attr.stat[LSB_DATA].st_gid
362 					= *(gid_t*)(buffer + bytePos);
363 				bytePos += 4;
364 				node->attr.stat[MSB_DATA].st_gid
365 					= *(gid_t*)(buffer + bytePos);
366 				bytePos += 4;
367 				break;
368 			}
369 
370 			case 'PN':
371 				TRACE(("RR: found PN, length %u\n", length));
372 				break;
373 
374 			// Symbolic link info
375 			case 'SL':
376 			{
377 				uint8 bytePos = 3;
378 				uint8 addPos = 0;
379 				bool slDone = false;
380 				bool useSeparator = true;
381 
382 				TRACE(("RR: found SL, length %u\n", length));
383 				TRACE(("Buffer is at %p\n", buffer));
384 				TRACE(("Current length is %u\n", slNameSize));
385 				//kernel_debugger("");
386 				node->attr.slVer = *(uint8*)(buffer + bytePos++);
387 #if TRACE_ISO9660
388 				uint8 slFlags = *(uint8*)(buffer + bytePos++);
389 				TRACE(("sl flags are %u\n", slFlags));
390 #else
391 				// skip symlink flags
392 				++bytePos;
393 #endif
394 				while (!slDone && bytePos < length) {
395 					uint8 compFlag = *(uint8*)(buffer + bytePos++);
396 					uint8 compLen = *(uint8*)(buffer + bytePos++);
397 
398 					if (slName == NULL)
399 						useSeparator = false;
400 
401 					addPos = slNameSize;
402 
403 					TRACE(("sl comp flags are %u, length is %u\n", compFlag, compLen));
404 					TRACE(("Current name size is %u\n", slNameSize));
405 
406 					switch (compFlag) {
407 						case SLCP_CONTINUE:
408 							useSeparator = false;
409 						default:
410 							// Add the component to the total path.
411 							slNameSize += compLen;
412 							if (useSeparator)
413 								slNameSize++;
414 							newSlName = (char*)realloc(slName,
415 								slNameSize + 1);
416 							if (newSlName == NULL) {
417 								free(slName);
418 								return B_NO_MEMORY;
419 							}
420 							slName = newSlName;
421 
422 							if (useSeparator) {
423 								TRACE(("Adding separator\n"));
424 								slName[addPos++] = '/';
425 							}
426 
427 							TRACE(("doing memcopy of %u bytes at offset %d\n", compLen, addPos));
428 							memcpy(slName + addPos, buffer + bytePos,
429 								compLen);
430 
431 							addPos += compLen;
432 							useSeparator = true;
433 							break;
434 
435 						case SLCP_CURRENT:
436 							TRACE(("InitNode - found link to current directory\n"));
437 							slNameSize += 2;
438 							newSlName = (char*)realloc(slName,
439 								slNameSize + 1);
440 							if (newSlName == NULL) {
441 								free(slName);
442 								return B_NO_MEMORY;
443 							}
444 							slName = newSlName;
445 
446 							memcpy(slName + addPos, "./", 2);
447 							useSeparator = false;
448 							break;
449 
450 						case SLCP_PARENT:
451 							slNameSize += 3;
452 							newSlName = (char*)realloc(slName,
453 								slNameSize + 1);
454 							if (newSlName == NULL) {
455 								free(slName);
456 								return B_NO_MEMORY;
457 							}
458 							slName = newSlName;
459 
460 							memcpy(slName + addPos, "../", 3);
461 							useSeparator = false;
462 							break;
463 
464 						case SLCP_ROOT:
465 							TRACE(("InitNode - found link to root directory\n"));
466 							slNameSize += 1;
467 							newSlName = (char*)realloc(slName,
468 								slNameSize + 1);
469 							if (newSlName == NULL) {
470 								free(slName);
471 								return B_NO_MEMORY;
472 							}
473 							slName = newSlName;
474 							memcpy(slName + addPos, "/", 1);
475 							useSeparator = false;
476 							break;
477 
478 						case SLCP_VOLROOT:
479 							slDone = true;
480 							break;
481 
482 						case SLCP_HOST:
483 							slDone = true;
484 							break;
485 					}
486 					if (slName != NULL)
487 						slName[slNameSize] = '\0';
488 					bytePos += compLen;
489 					TRACE(("Current sl name is \'%s\'\n", slName));
490 				}
491 				node->attr.slName = slName;
492 				TRACE(("InitNode = symlink name is \'%s\'\n", slName));
493 				break;
494 			}
495 
496 			// Altername name
497 			case 'NM':
498 			{
499 				uint8 bytePos = 3;
500 				uint8 flags = 0;
501 				uint16 oldEnd = altNameSize;
502 
503 				altNameSize += length - 5;
504 				char* newAltName = (char*)realloc(altName, altNameSize + 1);
505 				if (newAltName == NULL) {
506 					free(altName);
507 					return B_NO_MEMORY;
508 				}
509 				altName = newAltName;
510 
511 				TRACE(("RR: found NM, length %u\n", length));
512 				// Read flag and version.
513 				node->attr.nmVer = *(uint8 *)(buffer + bytePos++);
514 				flags = *(uint8 *)(buffer + bytePos++);
515 
516 				TRACE(("RR: nm buffer is %s, start at %p\n", (buffer + bytePos),
517 					buffer + bytePos));
518 
519 				// Build the file name.
520 				memcpy(altName + oldEnd, buffer + bytePos, length - 5);
521 				altName[altNameSize] = '\0';
522 				TRACE(("RR: alt name is %s\n", altName));
523 
524 				// If the name is not continued in another record, update
525 				// the record name.
526 				if (!(flags & NM_CONTINUE)) {
527 					// Get rid of the ISO name, replace with RR name.
528 					if (node->name != NULL)
529 						free(node->name);
530 					node->name = altName;
531 					node->name_length = altNameSize;
532 				}
533 				break;
534 			}
535 
536 			// Deep directory record masquerading as a file.
537 			case 'CL':
538 			{
539 				TRACE(("RR: found CL, length %u\n", length));
540 				// Reinitialize the node with the information at the
541 				// "." entry of the pointed to directory data
542 				node->startLBN[LSB_DATA] = *(uint32*)(buffer+4);
543 				node->startLBN[MSB_DATA] = *(uint32*)(buffer+8);
544 
545 				char* buffer = (char*)block_cache_get(volume->fBlockCache,
546 					node->startLBN[FS_DATA_FORMAT]);
547 				if (buffer == NULL)
548 					break;
549 
550 				InitNode(volume, node, buffer, NULL, true);
551 				block_cache_put(volume->fBlockCache,
552 					node->startLBN[FS_DATA_FORMAT]);
553 				break;
554 			}
555 
556 			case 'PL':
557 				TRACE(("RR: found PL, length %u\n", length));
558 				break;
559 
560 			case 'RE':
561 				// Relocated directory, we should skip.
562 				TRACE(("RR: found RE, length %u\n", length));
563 				if (!relocated)
564 					return B_UNSUPPORTED;
565 				break;
566 
567 			case 'TF':
568 				TRACE(("RR: found TF, length %u\n", length));
569 				break;
570 
571 			case 'RR':
572 				TRACE(("RR: found RR, length %u\n", length));
573 				break;
574 
575 			case 'SF':
576 				TRACE(("RR: found SF, sparse files not supported!\n"));
577 				// TODO: support sparse files
578 				return B_UNSUPPORTED;
579 
580 			default:
581 				if (buffer[0] == '\0') {
582 					TRACE(("RR: end of extensions\n"));
583 					done = true;
584 				} else
585 					TRACE(("RR: Unknown tag %c%c\n", buffer[0], buffer[1]));
586 				break;
587 		}
588 	}
589 
590 	return B_OK;
591 }
592 
593 //	#pragma mark - ISO-9660 specific exported functions
594 
595 
596 status_t
597 ISOMount(const char *path, uint32 flags, iso9660_volume **_newVolume,
598 	bool allowJoliet)
599 {
600 	// path: 		path to device (eg, /dev/disk/scsi/030/raw)
601 	// partition:	partition number on device ????
602 	// flags:		currently unused
603 
604 	// determine if it is an ISO volume.
605 	char buffer[ISO_PVD_SIZE];
606 	bool done = false;
607 	bool isISO = false;
608 	off_t offset = 0x8000;
609 	ssize_t retval;
610 	partition_info partitionInfo;
611 	int deviceBlockSize;
612 	iso9660_volume *volume;
613 
614 	(void)flags;
615 
616 	TRACE(("ISOMount - ENTER\n"));
617 
618 	volume = (iso9660_volume *)calloc(sizeof(iso9660_volume), 1);
619 	if (volume == NULL) {
620 		TRACE(("ISOMount - mem error \n"));
621 		return B_NO_MEMORY;
622 	}
623 
624 	memset(&partitionInfo, 0, sizeof(partition_info));
625 
626 	/* open and lock the device */
627 	volume->fdOfSession = open(path, O_RDONLY);
628 
629 	/* try to open the raw device to get access to the other sessions as well */
630 	if (volume->fdOfSession >= 0) {
631 		if (ioctl(volume->fdOfSession, B_GET_PARTITION_INFO, &partitionInfo,
632 				sizeof(partition_info)) < 0) {
633 			TRACE(("B_GET_PARTITION_INFO: ioctl returned error\n"));
634 			strcpy(partitionInfo.device, path);
635 		}
636 		TRACE(("ISOMount: open device/file \"%s\"\n", partitionInfo.device));
637 
638 		volume->fd = open(partitionInfo.device, O_RDONLY);
639 	}
640 
641 	if (volume->fdOfSession < 0 || volume->fd < 0) {
642 		close(volume->fd);
643 		close(volume->fdOfSession);
644 
645 		TRACE(("ISO9660 ERROR - Unable to open <%s>\n", path));
646 		free(volume);
647 		return B_BAD_VALUE;
648 	}
649 
650 	deviceBlockSize = get_device_block_size(volume->fdOfSession);
651 	if (deviceBlockSize < 0)  {
652 		TRACE(("ISO9660 ERROR - device block size is 0\n"));
653 		close(volume->fd);
654 		close(volume->fdOfSession);
655 
656 		free(volume);
657 		return B_BAD_VALUE;
658 	}
659 
660 	volume->joliet_level = 0;
661 	while (!done && offset < 0x10000) {
662 		retval = read_pos(volume->fdOfSession, offset, (void*)buffer,
663 			ISO_PVD_SIZE);
664 		if (retval < ISO_PVD_SIZE) {
665 			isISO = false;
666 			break;
667 		}
668 
669 		if (strncmp(buffer + 1, kISO9660IDString, 5) == 0) {
670 			if (*buffer == 0x01 && !isISO) {
671 				// ISO_VD_PRIMARY
672 				off_t maxBlocks;
673 
674 				TRACE(("ISOMount: Is an ISO9660 volume, initting rec\n"));
675 
676 				InitVolDesc(volume, buffer);
677 				strncpy(volume->devicePath,path,127);
678 				volume->id = ISO_ROOTNODE_ID;
679 				TRACE(("ISO9660: volume->blockSize = %d\n", volume->logicalBlkSize[FS_DATA_FORMAT]));
680 
681 #if TRACE_ISO9660
682 				int multiplier = deviceBlockSize / volume->logicalBlkSize[FS_DATA_FORMAT];
683 				TRACE(("ISOMount: block size multiplier is %d\n", multiplier));
684 #endif
685 
686 				// if the session is on a real device, size != 0
687 				if (partitionInfo.size != 0) {
688 					maxBlocks = (partitionInfo.size + partitionInfo.offset)
689 						/ volume->logicalBlkSize[FS_DATA_FORMAT];
690 				} else
691 					maxBlocks = volume->volSpaceSize[FS_DATA_FORMAT];
692 
693 				/* Initialize access to the cache so that we can do cached i/o */
694 				TRACE(("ISO9660: cache init: dev %d, max blocks %Ld\n", volume->fd, maxBlocks));
695 				volume->fBlockCache = block_cache_create(volume->fd, maxBlocks,
696 					volume->logicalBlkSize[FS_DATA_FORMAT], true);
697 				isISO = true;
698 			} else if (*buffer == 0x02 && isISO && allowJoliet) {
699 				// ISO_VD_SUPPLEMENTARY
700 
701 				// JOLIET extension
702 				// test escape sequence for level of UCS-2 characterset
703 			    if (buffer[88] == 0x25 && buffer[89] == 0x2f) {
704 					switch (buffer[90]) {
705 						case 0x40: volume->joliet_level = 1; break;
706 						case 0x43: volume->joliet_level = 2; break;
707 						case 0x45: volume->joliet_level = 3; break;
708 					}
709 
710 					TRACE(("ISO9660 Extensions: Microsoft Joliet Level %d\n", volume->joliet_level));
711 
712 					// Because Joliet-stuff starts at other sector,
713 					// update root directory record.
714 					if (volume->joliet_level > 0) {
715 						InitNode(volume, &volume->rootDirRec, &buffer[156],
716 							NULL);
717 					}
718 				}
719 			} else if (*(unsigned char *)buffer == 0xff) {
720 				// ISO_VD_END
721 				done = true;
722 			} else
723 				TRACE(("found header %d\n",*buffer));
724 		}
725 		offset += 0x800;
726 	}
727 
728 	if (!isISO) {
729 		// It isn't an ISO disk.
730 		close(volume->fdOfSession);
731 		close(volume->fd);
732 		free(volume);
733 
734 		TRACE(("ISOMount: Not an ISO9660 volume!\n"));
735 		return B_BAD_VALUE;
736 	}
737 
738 	TRACE(("ISOMount - EXIT, returning %p\n", volume));
739 	*_newVolume = volume;
740 	return B_OK;
741 }
742 
743 
744 /*!	Reads in a single directory entry and fills in the values in the
745 	dirent struct. Uses the cookie to keep track of the current block
746 	and position within the block. Also uses the cookie to determine when
747 	it has reached the end of the directory file.
748 */
749 status_t
750 ISOReadDirEnt(iso9660_volume *volume, dircookie *cookie, struct dirent *dirent,
751 	size_t bufferSize)
752 {
753 	int	result = B_NO_ERROR;
754 	bool done = false;
755 
756 	TRACE(("ISOReadDirEnt - ENTER\n"));
757 
758 	while (!done) {
759 		off_t totalRead = cookie->pos + (cookie->block - cookie->startBlock)
760 			* volume->logicalBlkSize[FS_DATA_FORMAT];
761 
762 		// If we're at the end of the data in a block, move to the next block.
763 		char *blockData;
764 		while (true) {
765 			blockData
766 				= (char*)block_cache_get(volume->fBlockCache, cookie->block);
767 			if (blockData != NULL && *(blockData + cookie->pos) == 0) {
768 				// NULL data, move to next block.
769 				block_cache_put(volume->fBlockCache, cookie->block);
770 				blockData = NULL;
771 				totalRead
772 					+= volume->logicalBlkSize[FS_DATA_FORMAT] - cookie->pos;
773 				cookie->pos = 0;
774 				cookie->block++;
775 			} else
776 				break;
777 
778 			if (totalRead >= cookie->totalSize)
779 				break;
780 		}
781 
782 		off_t cacheBlock = cookie->block;
783 
784 		if (blockData != NULL && totalRead < cookie->totalSize) {
785 			iso9660_inode node;
786 			size_t bytesRead = 0;
787 			result = InitNode(volume, &node, blockData + cookie->pos,
788 				&bytesRead);
789 
790 			// if we hit an entry that we don't support, we just skip it
791 			if (result != B_OK && result != B_UNSUPPORTED)
792 				break;
793 
794 			if (result == B_OK && (node.flags & ISO_IS_ASSOCIATED_FILE) == 0) {
795 				size_t nameBufferSize = bufferSize - offsetof(struct dirent, d_name);
796 
797 				dirent->d_dev = volume->id;
798 				dirent->d_ino = ((ino_t)cookie->block << 30)
799 					+ (cookie->pos & 0x3fffffff);
800 				dirent->d_reclen = offsetof(struct dirent, d_name) + node.name_length + 1;
801 
802 				if (node.name_length <= nameBufferSize) {
803 					// need to do some size checking here.
804 					strlcpy(dirent->d_name, node.name, node.name_length + 1);
805 					TRACE(("ISOReadDirEnt  - success, name is %s, block %Ld, "
806 						"pos %Ld, inode id %Ld\n", dirent->d_name, cookie->block,
807 						cookie->pos, dirent->d_ino));
808 				} else {
809 					// TODO: this can be just normal if we support reading more
810 					// than one entry.
811 					TRACE(("ISOReadDirEnt - ERROR, name %s does not fit in "
812 						"buffer of size %d\n", node.name, (int)nameBufferSize));
813 					result = B_BAD_VALUE;
814 				}
815 
816 				done = true;
817 			}
818 
819 			cookie->pos += bytesRead;
820 
821 			if (cookie->pos == volume->logicalBlkSize[FS_DATA_FORMAT]) {
822 				cookie->pos = 0;
823 				cookie->block++;
824 			}
825 		} else {
826 			if (totalRead >= cookie->totalSize)
827 				result = B_ENTRY_NOT_FOUND;
828 			else
829 				result = B_NO_MEMORY;
830 			done = true;
831 		}
832 
833 		if (blockData != NULL)
834 			block_cache_put(volume->fBlockCache, cacheBlock);
835 	}
836 
837 	TRACE(("ISOReadDirEnt - EXIT, result is %s, vnid is %Lu\n",
838 		strerror(result), dirent->d_ino));
839 
840 	return result;
841 }
842 
843 
844 status_t
845 InitNode(iso9660_volume* volume, iso9660_inode* node, char* buffer,
846 	size_t* _bytesRead, bool relocated)
847 {
848 	uint8 recordLength = *(uint8*)buffer++;
849 	size_t nameLength;
850 
851 	TRACE(("InitNode - ENTER, bufstart is %p, record length is %d bytes\n",
852 		buffer, recordLength));
853 
854 	if (_bytesRead != NULL)
855 		*_bytesRead = recordLength;
856 	if (recordLength == 0)
857 		return B_ENTRY_NOT_FOUND;
858 
859 	char* end = buffer + recordLength;
860 
861 	if (!relocated) {
862 		node->cache = NULL;
863 		node->name = NULL;
864 		node->attr.slName = NULL;
865 		memset(node->attr.stat, 0, sizeof(node->attr.stat));
866 	} else
867 		free(node->attr.slName);
868 
869 	node->extAttrRecLen = *(uint8*)buffer++;
870 	TRACE(("InitNode - ext attr length is %d\n", (int)node->extAttrRecLen));
871 
872 	node->startLBN[LSB_DATA] = *(uint32*)buffer;
873 	buffer += 4;
874 	node->startLBN[MSB_DATA] = *(uint32*)buffer;
875 	buffer += 4;
876 	TRACE(("InitNode - data start LBN is %d\n",
877 		(int)node->startLBN[FS_DATA_FORMAT]));
878 
879 	node->dataLen[LSB_DATA] = *(uint32*)buffer;
880 	buffer += 4;
881 	node->dataLen[MSB_DATA] = *(uint32*)buffer;
882 	buffer += 4;
883 	TRACE(("InitNode - data length is %d\n",
884 		(int)node->dataLen[FS_DATA_FORMAT]));
885 
886 	init_node_date(&node->recordDate, buffer);
887 	buffer += 7;
888 
889 	node->flags = *(uint8*)buffer;
890 	buffer++;
891 	TRACE(("InitNode - flags are %d\n", node->flags));
892 
893 	node->fileUnitSize = *(uint8*)buffer;
894 	buffer++;
895 	TRACE(("InitNode - fileUnitSize is %d\n", node->fileUnitSize));
896 
897 	node->interleaveGapSize = *(uint8*)buffer;
898 	buffer++;
899 	TRACE(("InitNode - interleave gap size = %d\n", node->interleaveGapSize));
900 
901 	node->volSeqNum = *(uint32*)buffer;
902 	buffer += 4;
903 	TRACE(("InitNode - volume seq num is %d\n", (int)node->volSeqNum));
904 
905 	nameLength = *(uint8*)buffer;
906 	buffer++;
907 
908 	// for relocated directories we take the name from the placeholder entry
909 	if (!relocated) {
910 		node->name_length = nameLength;
911 		TRACE(("InitNode - file id length is %" B_PRIu32 "\n",
912 			node->name_length));
913 	}
914 
915 	// Set defaults, in case there is no RockRidge stuff.
916 	node->attr.stat[FS_DATA_FORMAT].st_mode |= (node->flags & ISO_IS_DIR) != 0
917 		? S_IFDIR | S_IXUSR | S_IRUSR | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH
918 		: S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
919 
920 	if (node->name_length == 0) {
921 		TRACE(("InitNode - File ID String is 0 length\n"));
922 		return B_ENTRY_NOT_FOUND;
923 	}
924 
925 	if (!relocated) {
926 		// JOLIET extension:
927 		// on joliet discs, buffer[0] can be 0 for Unicoded filenames,
928 		// so I've added a check here to test explicitely for
929 		// directories (which have length 1)
930 		// Take care of "." and "..", the first two dirents are
931 		// these in iso.
932 		if (node->name_length == 1 && buffer[0] == 0) {
933 			node->name = strdup(".");
934 			node->name_length = 1;
935 		} else if (node->name_length == 1 && buffer[0] == 1) {
936 			node->name = strdup("..");
937 			node->name_length = 2;
938 		} else if (volume->joliet_level > 0) {
939 			// JOLIET extension: convert Unicode16 string to UTF8
940 			// Assume that the unicode->utf8 conversion produces 4 byte
941 			// utf8 characters, and allocate that much space
942 			node->name = (char*)malloc(node->name_length * 2 + 1);
943 			if (node->name == NULL)
944 				return B_NO_MEMORY;
945 
946 			int32 sourceLength = node->name_length;
947 			int32 destLength = node->name_length * 2;
948 
949 			status_t status = unicode_to_utf8(buffer, &sourceLength,
950 				node->name, &destLength);
951 			if (status < B_OK) {
952 				dprintf("iso9660: error converting unicode->utf8\n");
953 				return status;
954 			}
955 
956 			node->name[destLength] = '\0';
957 			node->name_length = destLength;
958 
959 			sanitize_iso_name(node, false);
960 		} else {
961 			node->name = (char*)malloc(node->name_length + 1);
962 			if (node->name == NULL)
963 				return B_NO_MEMORY;
964 
965 			// convert all characters to lower case
966 			for (uint32 i = 0; i < node->name_length; i++)
967 				node->name[i] = tolower(buffer[i]);
968 
969 			node->name[node->name_length] = '\0';
970 
971 			sanitize_iso_name(node, true);
972 		}
973 
974 		if (node->name == NULL) {
975 			TRACE(("InitNode - unable to allocate memory!\n"));
976 			return B_NO_MEMORY;
977 		}
978 	}
979 
980 	buffer += nameLength;
981 	if (nameLength % 2 == 0)
982 		buffer++;
983 
984 	TRACE(("DirRec ID String is: %s\n", node->name));
985 
986 	return parse_rock_ridge(volume, node, buffer, end, relocated);
987 }
988 
989 
990 status_t
991 ConvertRecDate(ISORecDate* inDate, time_t* outDate)
992 {
993 	time_t	time;
994 	int		days, i, year;
995 	int8_t tz;
996 
997 	year = inDate->year - 70;
998 	tz = inDate->offsetGMT;
999 
1000 	if (year < 0) {
1001 		time = 0;
1002 	} else {
1003 		const int monlen[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1004 
1005 		days = (year * 365);
1006 
1007 		if (year > 2)
1008 			days += (year + 1)/ 4;
1009 
1010 		for (i = 1; (i < inDate->month) && (i < 12); i++) {
1011 			days += monlen[i-1];
1012 		}
1013 
1014 		if (((year + 2) % 4) == 0 && inDate->month > 2)
1015 			days++;
1016 
1017 		days += inDate->date - 1;
1018 		time = ((((days*24) + inDate->hour) * 60 + inDate->minute) * 60)
1019 					+ inDate->second;
1020 
1021 		if (-48 <= tz && tz <= 52)
1022 			time -= tz * 15 * 60;
1023 	}
1024 	*outDate = time;
1025 	return 0;
1026 }
1027 
1028